算法精炼每趟从待排序的记录中选出关键字最小的记录,顺序放在已排序的记录序列末尾,直到全部排序结束为止。
简单排序处理流程
或者:
- 从待排序序列中,找到关键字最大的元素;
- 如果最大元素不是待排序序列的最后一个元素,将其和最后一个元素互换;
- 从余下的 N - 1 个元素中,找出关键字最大的元素,重复1、2步,直到排序结束。
时间复杂度:
C语言代码实现:
#include
//最小值往前放
void SelectSort(int arr[], int len)
{
if (len < 1 || arr == nullptr) return;
for (int i = 0; i < len; i++)
{
int minindex = i;//用来保存最小值的索引
for (int j = i + 1; j < len; j++)
{
if (arr[j] < arr[index])
{
index = j;
}
}
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
//最大值往后放
void SelectSort1(int arr[], int len)
{
if (len < 1 || arr == nullptr) return;
for (int i = len-1; i > 0; --i)
{
int index = i;//用来保存最大值的索引
for (int j = 0; j < i; j++)
{
if (arr[j] > arr[index])
{
index = j;
}
}
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int len = sizeof(arr) / sizeof(arr[0]);
SelectSort(arr, len);
PrintAddr(arr, len);
}
算法精炼依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。
处理流程
时间复杂度:
C语言代码实现:
#include
void BubbleSort(int arr[],int len)
{
if (len < 1 || arr==nullptr) return;
for(int i=0;i<len;i++)
for (int j=0; j < len-i-1; j++)
{
if (arr[j]> arr[j + 1])
{
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 10,1,35,61,89,36,55};
int len = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, len);
PrintAddr(arr, len);
}
**算法精炼:**通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
具体描述:
例子:
C语言代码实现:
#include
void InsertSort(int arr[], int len)
{
for (int i = 1; i < len; i++)
{
int curVaule = arr[i]; //从第二个元素开始
int preIndex = i - 1; //第一个元素下标
while (preIndex >= 0 && arr[preIndex] > curVaule) //第一个数比新元素大
{
arr[preIndex + 1] = arr[preIndex]; //大的往后移
preIndex--; //继续判断前一个数
}
arr[preIndex + 1] = curVaule; //直到找到小于或者等于新元素的数,while循环里面减去了1判断前一个,所以要加上
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 171,161,163,165,167,169 };
int len = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, len);
PrintAddr(arr, len);
}
插入排序虽好,但是某些特殊情况也有很多缺点,比如像下面这种情况
169 前的元素基本不用插入操作就已经有序, 元素 1 和 2 的排序几乎要移动数组前面的所有元素!!! 于是,有个老帅哥就提出了优化此问题的希尔排序!
算法精炼:是希尔(Donald Shell)于 1959 年提出的一种排序算法。也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。它与插入排序的不同之处在于,它会优先比较距离较远的元素 。
基本步骤:
C语言代码实现
#include
void ShellSort(int arr[], int len)
{
for (int gap = len / 2; gap > 0; gap /= 2)
{
for (int i = gap; i<len; i++) //每个组执行插入排序
{
int curValue = arr[i]; //arr[i]就是第一组的第二个数据
int preIndex = i - gap; //自然这个就是第一组的第一个元素
while (preIndex >= 0 && arr[preIndex] > curValue)
{
arr[preIndex + gap] = arr[preIndex];
preIndex -= gap;
}
arr[preIndex + gap] = curValue;
}
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 8,9,1,7,2,3,5,4,6,0 };
int len = sizeof(arr) / sizeof(arr[0]);
ShellSort(arr, len);
PrintAddr(arr, len);
}
**算法精炼:**指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素
基本步骤:
C语言代码实现
void heapSort(Heap &heap){
if (heap.size<1) return ;
while(heap.size>0){
int tmp = heap.arr[0];
heap.arr[0] = heap.arr[heap.size-1];
heap.arr[heap.size-1] = tmp;
heap.size--;
adjustDown(heap, 0);// 向下执行堆调整
}
}
其中要用到堆的向下调整算法,因为当我们将数组进行一次选择排序后,就不满足最大堆了。所以找到堆最大元素比较麻烦,但调整到最大堆后,就很方便找到为heap.arr[0]
堆相关的知识:【数据结构篇C++实现】- 树
堆的向下调整算法:
/*向下调整将当前的节点和子节点调整成最大堆*/ void adjustDown(Heap &heap, int index) { int cur=heap.arr[index];//当前待调整的节点 int parent,child; /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整;如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,则要继续按照同样的步骤对这个子节点进行调整*/ for(parent=index; (parent*2+1)<heap.size; parent=child) { child=parent*2+1; //取两个子节点中的最大的节点 if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1])) { child++; } //判断最大的节点是否大于当前的父节点 if(cur>=heap.arr[child]) {//不大于,则不需要调整,跳出循环 break; }else {//大于当前的父节点,进行交换,然后从子节点位置继续向下调整 heap.arr[parent]=heap.arr[child]; heap.arr[child]=cur; } } }
算法精炼:归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分为(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)
分治法:【算法篇C++实现】五大常规算法
可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现):
C语言代码实现:
#include
#include
void MergeAdd(int arr[], int left, int mid, int right, int *temp)
{
int lmin = left; //指向左边数组最小元素位置
int rmin = mid; //指向右边数组最小元素位置(实参是mid+1)
int index = left; //临时数组下标
while (lmin<mid && rmin<=right)
{
if (arr[lmin] < arr[rmin])
{
temp[index++] = arr[lmin++];
}
else
{
temp[index++] = arr[rmin++];
}
}
//如果是右边数组先移完
while (lmin < mid)
temp[index++] = arr[lmin++];
//如果是左边数组先移完
while (rmin <= right)
temp[index++] = arr[rmin++];
memcpy(arr + left, temp + left, sizeof(int)*(right - left + 1));
}
void MergeSort(int arr[], int left, int right, int *temp)
{
if (left <0 || arr == nullptr) return;
if (left < right)
{
int mid = (right+left)/2;
MergeSort(arr, left, mid, temp); //左边一半递归实现
MergeSort(arr, mid+1, right, temp); //右边一半递归实现
MergeAdd(arr, left, mid + 1, right, temp); //分到最后,合并子列项
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 8,9,1,7,2,3,5,4,6,0 };
int len = sizeof(arr) / sizeof(arr[0]);
int *temp = new int[len];
MergeSort(arr,0,len-1,temp);
PrintAddr(arr, len);
delete temp;
}
**算法精炼:**每次选取第一个数为基准数;然后将大于和小于基准的元素分别放置于基准数两边–>“乾坤大挪移”;继续分别对基准数两侧未排序的数据使用分治法进行细分处理,直至整个序列有序。
C语言代码实现:
#include
int partition(int arr[], int low, int high)
{
int i = low;
int j = high;
int base = arr[low]; //选择第一个数作为基准
if (low < high)
{
while (i < j)
{
while (i < j && arr[j] >= base)
{
j--; //从右往左找到比基准数小的
}
if (i < j)//右边有比基数小的数(排除j
{
arr[i++] = arr[j];
}
while (i < j&&arr[i] < base)
{
i++; //从左往右找到比基准数大的
}
if (i < j)//左边有比基数大的数
{
arr[j--] = arr[i];
}
}
arr[i] = base;
}
return i;
}
//快速排序
void QuickSort(int arr[], int low, int high)
{
if (low < high)
{
int index = partition(arr, low, high); //快速排序
QuickSort(arr, low, index - 1); //左边一半快速排序
QuickSort(arr, index + 1, high); //右边一半快速排序
}
}
void PrintAddr(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main()
{
int arr[] = { 163, 161, 158, 165, 171, 170, 163, 159, 162 };
int len = sizeof(arr) / sizeof(arr[0]);
QuickSort(arr, 0, len - 1);
PrintAddr(arr, len);
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 排序方式 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n * n) | O(n) | O(n * n) | In-place | 稳定 |
选择排序 | O(n * n) | O(n * n) | O(n * n) | In-place | 不稳定 |
插入排序 | O(n * n) | O(n) | O(n * n) | In-place | 稳定 |
希尔排序 | O(n * log n) | O(n * log n) | O(n * log n) | In-place | 不稳定 |
归并排序 | O(n * log n) | O(n * log n) | O(n * log n) | Out-place | 稳定 |
堆排序 | O(n * log n) | O(n * log n) | O(n * log n) | In-place | 不稳定 |
快速排序 | O(n * log n) | O(n * log n) | O(n * n) | In-place | 不稳定 |