排序方法种类:
- 冒泡排序
- 插入排序
- 选择排序
- 归并排序
- 堆排序
- 快速排序
- 基数排序
分类方法:
- 稳定排序与非稳定排序:两个大小一样的值,在排序后相对位置不变。
- 内部排序与外部排序:是否需要开辟额外存储空间。
稳定排序:
- 冒泡排序 2. 插入排序 3. 归并排序 4. 基数排序
非稳定排序:
- 选择排序 2. 堆排序 3. 快速排序
稳定排序:
冒泡排序规则:
- 将数组切分为未排序区和已排序区。
- 从头到尾扫描未排序区,若前面元素比后面元素的 大 则交换。
- 每一轮都将待排序区的最大值放到已排序区的首部(最左侧)。
- 直到待排序区没有元素为止。
小优化:当某一轮没有出现两个元素交换则说明排序完成,直接跳出即可
代码:
void bubble_sort(int *num, int n) {
int flag;
for (int i = 0; i < n; ++i) {
flag = 0;
for (int j = 0; j < n - i; ++j) {
if (num[j] <= num[j + 1]) continue;
SWAP(num[j], num[j + 1]);
flag += 1;
}
if (flag == 0) break;
}
return;
}
插入排序规则:
- 将数组切分为已排序区和未排序区。
- 每一轮都将未排序区的首部元素插入到已排序区。
- 直到待排序区没有元素为止。
代码:
void insert_sort(int *num, int n) {
for (int i = 1; i < n; ++i) {
for (int j = i - 1; j >= 0; j--) {
if (num[j + 1] >= num[j]) break;
SWAP(num[j], num[j + 1]);
}
}
return ;
}
归并排序:
思考与分析
我们可以借鉴分治思想解决排序问题,我们知道简单插入排序的时间复杂度为
, 如若对于元素组切分为两半,然后对于每一半进行简单插入排序,再通过
进行合并,那么时间复杂度为多少呢?
我们只进行了一次分治,时间复杂度就大幅度降低,那么我们再对这两个数组接续分治将得到一个较优的排序策略。
代码:
void merge_sort(int *num, int l, int r) {
if (r - l < 16) {
insert_sort(num + l, r - l + 1);
return;
}
int mid = (l + r) >> 1;
merge_sort(num, l, mid);
merge_sort(num, mid + 1, r);
int *temp =(int*)malloc(sizeof(int) * (r - l + 1));
int p1 = l, p2 = mid + 1, k = 0;
while (p1 <= mid || p2 <= r) {
if (p1 <= mid && (num[p1] <= num[p2] || p2 > r)) {
temp[k++] = num[p1++];
} else {
temp[k++] = num[p2++];
}
}
memcpy(num + l, temp, sizeof(int) * r - l + 1);
free(temp);
return;
}
非稳定排序:
选择排序规则:
- 将数组切分为已排序区与未排序区。
- 每一轮将未排序区中的最小值放到已排序区的尾部。
- 直到未排序区没有元素为止。
代码:
int select_sort(int* nums, int n) {
for (int i = 0; i < n - 1; ++i) {
int pos = i, min_value = 0x7fffffff;
for (int j = i + 1; j < n; ++j) {
if (min_value > nums[j]) {
min_value = nums[j];
pos = j;
}
}
SWAP(nums[pos], nums[i]);
}
}
快速排序规则:
在快速排序中也采用了分治的思想。
首先我们需要确定一个基值,一般情况下我们选用最左侧的数组,然后使用双指针,一个指向最左侧节点,一个指向最右侧节点。首先右指针向左移动,当发现当前指向的数值小于基值,则与左指针指向的数值进行交换,然后左指针向右移动,当发现当前指向的数值大于基值,则与右指针指向的数值进行交换,然后右指针继续向左移动,直至两指针发生碰撞。碰撞位置指向的值则为基值。我们发现碰撞位置左侧的值都小于基值,碰撞位置右侧的值都大于基值。然后我们再以同样的策略分治解决左侧与右侧。
代码:
void quick_sort(int *nums, int l, int r) {
int p = l, q = r;
while (r - l >= 16) {
int pivot = nums[rand() % (r - l + 1) + l]; // 搅乱原数组 打消最坏情况
int p = l, q = r;
do {
while (nums[p] < pivot) ++p;
while (nums[q] > pivot) --q;
if (p < q) {
SWAP(nums[p], nums[q]);
++p, --q;
}
} while (p <= q);
quick_sort(nums, l, q);
l = p;
}
ungarded_insert_sort(nums + l, r - l + 1);
}
void ungarded_insert_sort(int* nums , int n) {
int ind = 0;
for (int i = 1; i < n; ++i) {
if (nums[i] < nums[ind]) ind = i;
}
SWAP(nums[ind], nums[0]);
for (int i = 2; i < n; ++i) {
int j = i;
while (nums[j] < nums[j - 1]) { //STL 非监督(减少n^2的边界条件判断)
SWAP(nums[j], nums[j - 1]);
j--;
}
}
return;
}