排序
选择排序
⚠️工作原理
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
🐯代码实现
这里我以实现升序为例。
void SelectSort(int* a, int n)
85 {
86 int left = 0;
87 int right = n - 1;
88
89 while (left < right)
90 {
//假设最大的最小的元素都是最左边的那个,
//循环找出真正的最大值和最小值。
91 int maxi =left;
92 int mini = left;
93 for (int i = left + 1; i <= right; i++)
94 {
95 if (a[i] > a[maxi])
96 maxi = i;
97 if (a[i] < a[mini])
98 mini = i;
99 }
//找出最小值后放到最左边
100 Swap(&a[left], &a[mini]);
101 //此处是坐标left和maxi相等
//如果最大值在最左边,把最小值放在左边后,
//这时最大值已经被覆盖了,需要修正。
102 if (left == maxi)
103 maxi = mini;
//把最大值放在最右边
104 Swap(&a[right], &a[maxi]);
105 left++;
106 right--;
107 }
108 }
插入排序
⚠️工作原理
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。
🐯代码实现
这里我以实现升序为例
void InsertSort(int* a, int n)
27 {
28 for (int i = 0; i < n-1; i++)
29 {
30 int end = i;
31 int tmp = a[end + 1];
32 //单趟排序,[0,end]有序,
//在a【end+1】处先放插入的新值,
33 while (end >= 0)
34 {
35 if (a[end] > tmp)
36 {
//如果新插入的值小,就让他和前面的数比较,
//让前面的数往后移
37 a[end + 1] = a[end];
38 end--;
39 }
40 else
41 {
42 break;
43 }
44
45 }
46 a[end + 1] = tmp;
47 }
48
49 }
希尔排序
⚠️工作原理
希尔排序是把记录按下标的一定增量(代码中用gap代替)分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
🐯代码实现
void ShellSort(int* a, int n);
51 //一个数组【2,7,53,2,1,8】
52 //分组,在每一组内部比较大小
53 void ShellSort(int* a, int n)
54 {
//gap就是增量
55 int gap = n;
56 while(gap>1)
57 {
//大量的实验表明,gap=3是不错的选择,
//此处gap每次除3,让增量变小,可以让结果更加精确,
//为了让每个数字都被分到组中,➕1就能解决这个问题
58 gap = gap / 3 + 1;
59 //i的范围是n-gap
60 for (int i = 0; i < n - gap; i++)
61 {
62 int end = i;
63 int tmp = a[end + gap];
64 while (end >= 0)
65 {
66 if (a[end] > tmp)
67 {
68 a[end + gap] =a[end];
69 end -= gap;
70 }
71 else
72 break;
75 }
76 a[end + gap] = tmp;
77 }
78 }
}
冒泡排序
⚠️工作原理
重复走访所有的元素,依次比较两个相邻的元素,如果顺序错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,排序完成。
🐯代码实现
void BubbleSort(int* a, int n)
152 {
//外循环,循环的次数是元素个数(n-1)次
//助记:假设有5个元素,第一次排序把最大的数沉到最后,
// 第二次排序把次大的数沉到最后
// .....
// 第四次排序把次小的沉到第二个位置
//最小的那个数就已经在第一位了
153 for (int j = 0; j < n-1; j++)
154 {
155 int i = 0;
156 int flag = 0;
157 for (i = 0; i < n-j; i++)
158 {
159 if (a[i] > a[i + 1])
160 {
161 Swap(&a[i], &a[i + 1]);
162 flag = 1;
163 }
164
165 }
166 if (flag == 0)
167 {
168 break;
169 }
170 }
171 }
堆排序
⚠️工作原理
堆排序运用了堆的性质,所有根节点大于(小于)他的子节点,要实现堆排序,只需要把原来的无序的二叉树调整成堆即可。
三步走实现堆:
1,把根节点的左右子树都调整成小堆(用向下调整算法)
2,将堆中的所有数据重新排序(从根节点开始向下调整)
3,移除位在第一个数据的根节点,再调整。
🐯代码实现
子函数(向下调整算法)
void AdjustDown(int* a, int n, int root)
113 {
114 int parent = root;
115 int child = parent * 2 + 1;
116 while (child < n )
117 {
118 if (a[child + 1] > a[child]&&(child+1<n))
119 child++;
120 if (a[parent] < a[child])
121 {
122 Swap(&a[parent], &a[child]);
123 parent = child;
124 child = parent * 2 + 1;
125 }
126 else
127 {
128 break;
129 }
130
131 }
132 }
void HeapSort(int* a, int n)
135 {
//从最后一个节点的父节点开始调整
136 for (int i = (n - 1 - 1 / 2); i >= 0; i--)
137 {
138 AdjustDown(a, n, i);
139 }
140 size_t end = n - 1;
141 while (end>0)
142 {
143 Swap(&a[0], &a[end]);
144 AdjustDown(a, end, 0);
145 end--;
146 }
147
148 }
快速排序
⚠️hoare(左右指针法)
🌲思路
1,选出一个key,一般是最左边或者最右边
2,定义两个指针left,right,left向右走,right向左走
(需要注意的是,如果key是最左边,那么right先走;如果key是最右边,left先走)
3,(选最右边的数是key)left先走,在走的过程中,如果遇到比key大的数,就停下来,right再走,right遇到比key小的数就停下来,然后交换left和right的值,交换后两个指针继续走,直到left和right相遇。最后将相遇点的数和key交换。
🐯代码实现
//左右指针法(hoare)
187 int partSort1(int* a,int left, int right)
188 {
189 //假设key是最右边的值
190 int keyindex = right;
191 while (left < right)
192 {
193 while(a[left] < a[keyindex]&&(left<right))
194 {
195 left++;
196 }
197 while (a[right] > a[keyindex]&&(left<right))
198 {
199 right--;
200 }
201 Swap(&a[left], &a[right]);
202 }
203 Swap(&a[left], &a[keyindex]);
204
205 return left;
206 }
207
⚠️挖坑法
🌲思路
1,选出一个key,一般是最左边或者最右边,此时key的位置就形成了一个坑。
2,定义两个指针left,right,left向右走,right向左走
(需要注意的是,如果key是最左边,那么right先走;如果key是最右边,left先走)
3,(选最右边的数是key)left先走,在走的过程中,如果遇到比key大的数,就把这个数放到坑里,这个数的位置就是新的坑。right再走,right遇到比key小的数就停下来,把这个数放到坑里,这个位置再次形成新的坑。直到left和right相遇。最后将相遇点赋值为key。
🐯代码实现
//挖坑法
209 int partSort2(int* a, int left, int right)
210 {
211 int key =a[ right];
//pit就是坑
212 int pit = right;
213 while (left < right)
214 {
215 while (a[left] < key && left < right)
216 left++;
217 a[pit] = a[left];
218 pit = left;
219
220 while (a[right] > key && left < right)
221 right--;
222 a[pit] = a[right];
223 pit = right;
224 }
225 a[pit] = key;
226 return pit;
227
228 }
229
前后指针法
🌲思路
1,选出一个key,一般是最左边或者最右边
2,定义一个指针cur从第一个元素开始走,另一个指针prev指向cur的前一个元素。
3,(选最右边的数是key)cur遇到比key大的停下,prev++,然后交换prev和cur的值,然后cur继续走,直到cur走到倒数第二个数,循环停止,prev++,交换prev和key。
🐯代码实现
//前后指针法
231 int partSort3(int* a, int left, int right)
232 {
233 int keyi = right;
234 int prev = left-1;
235 int cur = left ;
236 while (cur < right)
237 {
238 if (a[cur] < a[keyi] && a[prev++] != a[cur])
239 {
240 Swap(&a[prev], &a[cur]);
241 }
242 cur++;
243 }
244 Swap(&a[prev], &a[keyi]);
245 return prev;
246 }
归并排序
⚠️工作原理
把原来无序的数组分成几个子序,再把子序分成更小的子序,直到子序不能再分,然后给子序排序,直到原数组有序。
🐯 代码实现
子函数
void _MergeSort(int* a, int begin, int end, int* tmp)
{
if (begin >= end)
return;
//把一个区间分成两个小的区间
int mid = (begin + end) / 2;
// 新的小区间 [begin, mid][mid+1, end]
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid+1, end, tmp);
// 归并[begin, mid][mid+1, end]
//printf("归并[%d,%d][%d,%d]\n", begin, mid, mid+1, end);
int begin1 = begin, end1 = mid;
int begin2 = mid+1, end2 = end;
int index = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
tmp[index++] = a[begin1++];
else
tmp[index++] = a[begin2++];
}
while (begin1 <= end1)
tmp[index++] = a[begin1++];
while (begin2 <= end2)
tmp[index++] = a[begin2++];
memcpy(a+begin, tmp+begin, (end - begin + 1)*sizeof(int));
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
assert(tmp);
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
计数排序
⚠️工作原理
遍历数组,找出最小,最大的数,作为区间的头和尾,然后把数组的每个数对应放到这个区间里,放好之后,再一一读取出来放到原数组。
🐯代码实现
注意数要放到区间的相对位置上。
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] < min)
min = a[i];
if (a[i] > max)
max = a[i];
}
int range = max - min + 1;
int* countA = (int*)malloc(sizeof(int)*range);
assert(countA);
memset(countA, 0, sizeof(int)*range);
// 计数
for (int i = 0; i < n; ++i)
{
countA[a[i] - min]++;
}
// 排序
int j = 0;
for (int i = 0; i < range; ++i)
{
while (countA[i]--)
{
a[j++] = i + min;
}
}
}