============================
壹.定义
1.稳定性:排序前后,数值相同的数据,它们的相对位置没有发生变化
2.内部排序:数据元素全部存放在内存中的排序
3.外部排序:先排序一部分,再进行合并
============================
贰.排序算法
计数排序
---------------------- 各种排序的时间,空间复杂度 -----------------------------------
一.插入排序
1.插入排序(在已经排好序的序列中插入一个新的)
void insertSort(int* arr, int n) {
for (int i = 1;i < n;i++) {
int cur = i - 1;
int end = arr[i];
//向前遍历,比较当前数和要插入的数,并寻找要插入的位置
//在该位置后每个都向后推一个位置
while (cur >= 0 && arr[cur] >= end) {
arr[cur + 1] = arr[cur];
--cur;
}
arr[cur + 1] = end;
}
}
2.希尔排序(分组的插入排序)
void shellSort(int* arr,int n){
int gap = n;
while(gap > 1){
gap = gap / 3 + 1;
for(int i = gap;i < n;i++){
int end = i - gap;
int data = arr[i];
while(end >= 0 && arr[end] >data){
arr[end + gap] = arr[end];
end -= gap;
}
arr[end + gap] = data;
}
}
}
二.选择排序
1.选择排序(挑最大/最小的放到数据的头部/尾部,两数据位置交换)
void selectSort(int* arr,int n){
int start = 0;
int i;
for(i = start + 1;i <= end;++i){
if(arr[i] < arr[minIdx])
minIdx = i;
}
Swap(arr, start, minIdx);
++start;
}
2.堆排序
void shiftDown(int* arr,int n,int parent){
int child = 2 * parent + 1;
while(child < n){
if(child + 1 < n && arr[child + 1] > arr[child])
++child;
if(arr[child] > arr[parent]){
swap(arr, child, parent);
parent = child;
chile = 2 * parent + 1;
}
else
break;
}
}
void heapSort(int* arr, int n){
for(int i = (n - 2)/2;i >= 0;i--)
shiftDown(arr, n, i);
int end = n - 1;
while(end > 0){
swap(arr, end, 0);
shiftDown(arr, end, 0);
--end;
}
}
三.交换排序
*1.冒泡排序
void bubbleSort(int* arr,int n){
int end = n;
while(end > 1){
for(int i = 1;i < end;i++){
swap(arr, i - 1, i);
}
}
--end;
}
*2.快速排序
①horae法
- 递增型
选取一个基准值
从剩余元素中开始遍历:
1.从后往前找到第一个小于基准值的数据
2.从前往后找到第一个大于基准值的数据
3.交换找到的两个值
4.从交换的位置,分别开始,继续执行第一步
结束:用相遇位置的数据和基准值进行交换
//返回划分之后,基准值所在的位置
//未优化时,可能会导致栈溢出
int partion(int* arr,int begin,int end){
//选择基准值
int key = arr[begin];
int start = begin;
while(begin < end){
//从后向前先找小于基准值的位置
while(arr[end] >= key)
--end;
//从前向后找大于基准值的位置
while(arr[begin] <= key)
++begin;
//交换
Swap(arr, begin, end);
}
//交换基准值和相遇位置的数据
Swap(arr, begin, end);
return begin;
}
void quickSort(int* arr, int begin, int end){
if(begin >= end)
return;
//div:一次划分之后,基准值位置
int div = partion(arr,begin,end);
//左右两部分进行快速排序
//[begin, div - 1]
//[div + 1, end]
quickSort(arr,begin,div - 1);
quickSort(arr,div + 1,end);
}
//优化
int getMid(int* arr, int begin, int end){
int mid = begin + (end - begin) / 2;
if(arr[begin] > arr[mid]{
if(arr[mid] > arr[end])
return mid;
else if(arr[begin] >arr[end]) //mid end begin
return end;
else //mid begin end
return begin;
}
else{
//mid >= begin begin mid
if(arr[mid] < arr[end])
return mid;
else if(arr[begin] <arr[end]) //begin end mid
return end;
else //end begin mid
}
}
int partion(int* arr,int begin,int end){
int mid = getMid(arr, begin, end);
Swap(arr, begin, mid);
//选择基准值
int key = arr[begin];
int start = begin;
while(begin < end){
//从后向前先找小于基准值的位置
while(arr[end] >= key)
--end;
//从前向后找大于基准值的位置
while(arr[begin] <= key)
++begin;
//交换
Swap(arr, begin, end);
}
//交换基准值和相遇位置的数据
Swap(arr, begin, end);
return begin;
}
void quickSort(int* arr, int begin, int end){
if(begin >= end)
return;
//div:一次划分之后,基准值位置
int div = partion(arr,begin,end);
//左右两部分进行快速排序
//[begin, div - 1]
//[div + 1, end]
quickSort(arr,begin,div - 1);
quickSort(arr,div + 1,end);
}
- 递减型
1.从后往前找到第一个大于基准值的数据
2.从前往后找到第一个小于基准值的数据
②挖坑法
- 递增型
选取一个基准值
从剩余元素中开始遍历:
1.从后往前找到第一个小于基准值的数据,填到基准值的坑里(这个数据会产生一个新坑)
2.从前往后找到第一个大于基准值的数据,填到新坑里(这个数据会产生一个新坑)
3…从刚挖坑的位置,分别开始,继续执行第一步
结束:用一开始的基准值填到相遇位置,返回基准值的位置
int getMid(int* arr, int begin, int end){
int mid = begin + (end - begin) / 2;
if(arr[begin] > arr[mid]{
if(arr[mid] > arr[end])
return mid;
else if(arr[begin] >arr[end]) //mid end begin
return end;
else //mid begin end
return begin;
}
else{
//mid >= begin begin mid
if(arr[mid] < arr[end])
return mid;
else if(arr[begin] <arr[end]) //begin end mid
return end;
else //end begin mid
}
}
int partion(int* arr,int begin,int end){
int mid = getMid(arr, begin, end);
Swap(arr, begin, mid);
//选择基准值
int key = arr[begin];
while(begin < end){
//从后向前先找小于基准值的位置
while(arr[end] >= key)
--end;
//填坑
arr[begin] = arr[end];
//从前向后找大于基准值的位置
while(arr[begin] <= key)
++begin;
//填坑
arr[end] = arr[begin];
}
arr[begin] = key;
return begin;
}
void quickSort(int* arr, int begin, int end){
if(begin >= end)
return;
//div:一次划分之后,基准值位置
int div = partion(arr,begin,end);
//左右两部分进行快速排序
//[begin, div - 1]
//[div + 1, end]
quickSort(arr,begin,div - 1);
quickSort(arr,div + 1,end);
}
- 递减型
③前后指针法
- prev-----上一个小于基准值的位置(从key开始)
- cur-----下一个小于基准值的位置
当cur走到一个小于基准值的位置,则:
判断prev和cur是否连续:
-
如果连续,区间[prev, cur]的值都是不大于基准值(更新prev, cur)
-
如果不连续,区间[prev, cur]的值,有大于基准值的(更新prev,数据交换,更新cur)
数据交换:这时,用prev的下一个数和cur的数进行交换,然后prev和cur继续向后遍历
//一次划分
int partion(){
//上一个小于基准值的位置
int prev = begin;
//下一个大于基准值的位置
int cur = begin + 1;
int key = arr[begin];
while(cur <= end){
//当cur走到下一个小于基准值的位置,判断prev和cur是否连续
if(arr[cur] < key && ++prev != cur){
//不连续,交换数据: prev cur
Swap(arr, prev, cur);
}
++cur;
}
Swap(arr, begin, prev);
return prev;
}
//小区间优化-----小区间使用插入排序
void quickSort(int* arr, int begin, int end){
if(begin >= end)
return;
if(end - begin < 10){
insertSort(arr + begin, end - begin + 1);
}
//div:一次划分之后,基准值位置
int div = partion(arr,begin,end);
//左右两部分进行快速排序
//[begin, div - 1]
//[div + 1, end]
quickSort(arr,begin,div - 1);
quickSort(arr,div + 1,end);
}
④非递归快排
void quickSort(int* arr, int n){
Queue q;
initQueue(&q);
//保存[0, n - 1]区间
//队列,先进先出:先入左,再入右
queuePush(&q, 0);
queuePush(&q, n - 1);
while(!queueEmpty(&q)){
//取出一个区间的起始和结束位置
int left = queueFront(&q);
queuePop(&q);
int right = queueFront(&q);
queuePop(&q);
//划分[left, right]
int div = partion(arr, left, right);
//子区间[left, div - 1]
if(left < div - 1){
queuePush(&q, left);
queuePush(&q, div - 1);
}
//子区间[div - 1, right]
if(div + 1 < right){
queuePush(&q, div + 1);
queuePush(&q, right);
}
}
}
四.归并排序
1.归并排序
void mergeSort(int* arr, int n){
int* step = (int*)malloc(sizeof(int) * n);
//子序列的步长(自底向上的合并)
int step = 1;
while(step < n){
for(int idx = 0; idx < n; idx += 2 * step){
//找到两个待合并的子序列区间
//[begin, mid] [mid + 1, end]
int begin = idx;
int mid = idx + step - 1;
//判断是否存在第二个子序列
if(mid >= n - 1)
//不存在第二个子序列,直接跳过
continue;
int end = idx + 2 * step - 1;
//判断第二个子序列是否越界
if(end >= n)
end = n - 1;
merge(arr, begin, mid, end, tmp);
}
//更新步长
step *= 2;
}
}
============================
叁.不常见的排序
1.计数排序
时间复杂度:O(Max(n, range))
空间复杂度:O(range)
void countSort(in* arr, int n){
//找到最大和最小值
int max, min;
min = max = arr[0];
for(int i = 0; i < n;i++)[
if(arr[i] > max)
max = arr[i];
if(arr[i] < min)
min = arr[i];
}
//计算范围
int range = max - min + 1;
//创建一个计数数组,初始化为0
int* countArr = (int*)calloc(range, sizeof(int));
//计数
for(int i = 0;i < range;i++){
countArr[arr[i] - min]++;
}
//遍历计数数组,排序
int idx = 0;
for(int i = 0;i < range;i++){
while(countArr[i] --){
arr[idx++] = i + min;
}
}
}