七种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速)

首先声明一下,本文只对七种排序算法做简单总结,并参照一些资料给出自己的代码实现,并没有对某种算法理论讲解,更详细的

解可以参考以下资料(本人参考):

1、《data structure and algorithm analysis in c 》

2、《大话数据结构》

3、http://blog.csdn.net/morewindows/article/details/7961256


一、冒泡排序

基本思想是:两两比较相邻记录的关键字,如果反序则交换

冒泡排序时间复杂度最好的情况为O(n),最坏的情况是O(n^2) 

改进思路1:设置标志位,明显如果有一趟没有发生交换(flag = false),说明排序已经完成

改进思路2:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok


二、直接插入排序

将一个记录插入到已经排好序的有序表中, 从而得到一个新的,记录数增1的有序表 

时间复杂度也为O(n^2), 比冒泡法和选择排序的性能要更好一些


三、简单选择排序

通过n-i次关键字之间的比较,从n-i+1 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换之

 尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序


四、希尔排序

先将整个待排元素序列分割成若干子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排

序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。其时间复杂度为O(n^3/2),要好于直接

插入排序的O(n^2)


五、归并排序

假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(不小于n/2的最小整数)个长度为2

或1的有序子序列,再两两归并,...如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。 时间复杂度为

O(nlogn),空间复杂度为O(n+logn),如果非递归实现归并,则避免了递归时深度为logn的栈空间 空间复杂度为O(n)


六、堆排序

堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左

右孩子节点的值,称为小顶堆。


堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶 的根结点.将它移

走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元

素的次大值.如此反复执行,便能得到一个有序序列了。 时间复杂度为 O(nlogn),好于冒泡,简单选择,直接插入的O(n^2)


七、快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。时间复杂度为O(nlogn)

下文没有给出快速排序的实现,参考以前的文章




代码实现:(含3种swap交换函数,6个排序算法,不含快速排序)

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
 
#include<iostream>
using  namespace std;

void swap1( int *left,  int *right)
{
     int temp = *left;
    *left = *right;
    *right = temp;
}

void swap2( int &left,  int &right)
{
     int temp = left;
    left = right;
    right = left;
}

void swap3( int &left,  int &right)
{
     if (left != right)  //如果相同按下面方法会被置0
    {
        left ^= right;
        right ^= left;
        left ^= right;
    }
}

/*****************************************************************/
/* 冒泡排序时间复杂度最好的情况为O(n),最坏的情况是O(n^2)
* 基本思想是:两两比较相邻记录的关键字,如果反序则交换 */


void BubbleSort1( int arr[],  int num)
{
     int i, j;
     for (i =  0; i < num; i++)
    {
         for (j =  1; j < num - i; j++)
        {
             if (arr[j -  1] > arr[j])
                swap1(&arr[j -  1], &arr[j]);
        }
    }
}

// 改进思路:设置标志位,明显如果有一趟没有发生交换(flag = flase),说明排序已经完成.
void BubbleSort2( int arr[],  int num)
{
     int k = num;
     int j;
     bool flag =  true;
     while (flag)
    {
        flag =  false;
         for (j =  1; j < k; j++)
        {
             if (arr[j -  1] > arr[j])
            {
                swap1(&arr[j -  1], &arr[j]);
                flag =  true;
            }
        }
        k--;
    }
}
//改进思路:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok
void BubbleSort3( int arr[],  int num)
{
     int k, j;
     int flag = num;
     while (flag >  0)
    {
        k = flag;
        flag =  0;
         for (j =  1; j < k; j++)
        {
             if (arr[j -  1] > arr[j])
            {
                swap1(&arr[j -  1], &arr[j]);
                flag = j;
            }
        }
    }
}
/*************************************************************************/

/**************************************************************************/
/*插入排序: 将一个记录插入到已经排好序的有序表中, 从而得到一个新的,记录数增1的有序表
* 时间复杂度也为O(n^2), 比冒泡法和选择排序的性能要更好一些 */


void InsertionSort( int arr[],  int num)
{
     int temp;
     int i, j;
     for (i =  1; i < num; i++)
    {
        temp = arr[i];
         for (j = i; j >  0 && arr[j -  1] > temp; j--)
            arr[j] = arr[j -  1];
        arr[j] = temp;
    }
}

/****************************************************************************/

/*希尔排序:先将整个待排元素序列分割成若干子序列(由相隔某个“增量”的元素组成的)分别进行
直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,
再对全体元素进行一次直接插入排序。其时间复杂度为O(n^3/2),要好于直接插入排序的O(n^2) */


void ShellSort( int arr[],  int num)
{
     int i, j, gap, temp;
     for (gap = num /  2; gap >  0; gap /=  2//gap 的取值方法不一
    {
         for (i = gap; i < num; i++)
        {
            temp = arr[i];
             for (j = i - gap; j >=  0 && arr[j] > temp; j -= gap)
                arr[j + gap] = arr[j];
            arr[j + gap] = temp;
        }
    }
}

/**************************************************************************/

/* 简单选择排序(simple selection sort) 就是通过n-i次关键字之间的比较,从n-i+1
* 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换之
* 尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序 */


void SelectSort( int arr[],  int num)
{
     int i, j, Mindex;
     for (i =  0; i < num; i++)
    {
        Mindex = i;
         for (j = i +  1; j < num; j++)
        {
             if (arr[j] < arr[Mindex])
                Mindex = j;
        }

        swap1(&arr[i], &arr[Mindex]);
    }
}

/********************************************************************************/
/*假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后
* 两两归并,得到(不小于n/2的最小整数)个长度为2或1的有序子序列,再两两归并,...
* 如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序
* 时间复杂度为O(nlogn),空间复杂度为O(n+logn),如果非递归实现归并,则避免了递归时深度为logn的栈空间
* 空间复杂度为O(n) */



/*lpos is the start of left half, rpos is the start of right half*/
void merge( int a[],  int tmp_array[],  int lpos,  int rpos,  int rightn)
{
     int i, leftn, num_elements, tmpos;

    leftn = rpos -  1;
    tmpos = lpos;
    num_elements = rightn - lpos +  1;

     /*main loop*/
     while (lpos <= leftn && rpos <= rightn)
         if (a[lpos] <= a[rpos])
            tmp_array[tmpos++] = a[lpos++];
         else
            tmp_array[tmpos++] = a[rpos++];

     while (lpos <= leftn)  /*copy rest of the first part*/
        tmp_array[tmpos++] = a[lpos++];
     while (rpos <= rightn)  /*copy rest of the second part*/
        tmp_array[tmpos++] = a[rpos++];

     /*copy array back*/
     for (i =  0; i < num_elements; i++, rightn--)
        a[rightn] = tmp_array[rightn];
}


void msort( int a[],  int tmp_array[],  int left,  int right)
{
     int center;

     if (left < right)
    {
        center = (right + left) /  2;
        msort(a, tmp_array, left, center);
        msort(a, tmp_array, center +  1, right);
        merge(a, tmp_array, left, center +  1, right);
    }
}



void merge_sort( int a[],  int n)
{
     int *tmp_array;
    tmp_array = ( int *)malloc(n *  sizeof( int));

     if (tmp_array !=  NULL)
    {
        msort(a, tmp_array,  0, n -  1);
        free(tmp_array);
    }

     else
        printf( "No space for tmp array!\n");
}

/************************************************************************************/
/* 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;
* 或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆*/


/*堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶
* 的根结点.将它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新
* 构造成一个堆,这样就会得到n个元素的次大值.如此反复执行,便能得到一个有序序列了
*/

/* 时间复杂度为 O(nlogn),好于冒泡,简单选择,直接插入的O(n^2) */

// 构造大顶堆
void HeapAdjust( int arr[],  int i,  int num)
{
     int j, temp;
    temp = arr[i];
     for (j =  2 * i +  1; j < num; j =  2 * j +  1)
    {
         if (j +  1 < num && arr[j +  1] > arr[j])
            j++;  //如果右孩子比左孩子大,记录位置
         if (arr[j] <= temp)
             break//如果父节点大,没必要交换,直接退出循环
        arr[i] = arr[j];  //否则交换
        i = j;
    }
    arr[i] = temp;
}

void HeapSort( int arr[],  int num)
{
     int i;
     for (i = num /  2 -  1; i >=  0; i--)
        HeapAdjust(arr, i, num);  //构造大顶堆

     for (i = num -  1; i >=  1; i--)
    {
        swap1(&arr[i], &arr[ 0]);  //将堆顶放置最后
        HeapAdjust(arr,  0, i);  //重新构造大顶堆
    }
}


int main( void)
{
     int arr[] = {  92583471610};
    HeapSort(arr,  10);
     for ( int i =  0; i <  10; i++)
        cout << arr[i] <<  ' ';
    cout << endl;

     return  0;
}

 

 


 

posted on 2013-08-01 19:50  you Richer  阅读(511)  评论(0编辑  收藏  举报