Table of contents
The first type: digging method
The second type: left and right pointer method
The third type: fast and slow pointers
Divide and conquer recursion to achieve overall order
Non-recursive algorithm achieves overall ordering (understand)
Heap sort
Heap concept
To learn to use heap sorting, first understand the concept of a heap. A heap is a complete binary tree stored in an array. Its logical structure is a binary tree, and its physical structure is an array. And the value of the root of any subtree is the maximum or minimum value of its left child and right child. The heap is divided into a large heap and a small heap. A large heap: the data at the top of the heap is the largest, and a small heap: the data at the top of the heap is the smallest.
An example of a small heap is as follows:
A bunch of them are as follows:
Since the heap is stored sequentially as an array, we can easily index to a node.
The subscript relationship between father and son in the heap is:
leftchild == parent*2 + 1 ; rightchild == parent*2 + 2 == leftchild + 1 ;
parent == ( child - 1 ) / 2 ;
Implement heap sort
The first step is to build the heap:
Build a large heap (in ascending order) or a small heap (in descending order) in a bottom-up manner, starting from the last non-leaf node (the last parent node).
Take building a large pile as an example:
Implementation principle: Adjust the algorithm downward. If the value of parent is smaller than that of child in any binary tree, the larger value of the two children will be exchanged with parent until all subtrees in the binary tree satisfy the requirement of large heap.
//交换两个节点的值
void swap(int *p1, int*p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//向下调整算法 实现大堆
void ADjustDown(int *arr, int n, int root)
{
int parent = root;
int child = root * 2 + 1;
while (child < n)
{
if (child+1< n && (arr[child] < arr[child + 1])) //(child+1)注意越界、选出两个child中较大的值
{
child += 1;
}
if (arr[child] > arr[parent])
{
swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//实现堆排序
void HeapSort(int *arr, int n)
{
for (int i = (n - 1 - 1) / 2; i >= 0;--i) //自底向上 从最后一个parent节点开始调整,依次往上
{
ADjustDown(arr, n, i);
}//循环走完 建堆完成
//实现排序
int end = n - 1;
while (end > 0)
{
swap(&arr[end], &arr[0]); //将堆顶的数(该数组中最大的数)放到数组的组后一个位置
ADjustDown(arr, end, 0); //继续向下调整,实现大堆
end--;
}
//循环走完,排序完成
}
Time complexity of heap sort:
The time complexity of building the heap is O(n), the time complexity of sorting and selecting is O(logN), and the final time complexity is N*logN. It is a very efficient sorting algorithm.
Quick sort
concept
The first type: digging method
end, first go left. If you find a value smaller than key, put it into the pivot (pit). If the number is moved, a new pit will appear. Begin, go right again and find a value larger than key, put it into the pit. , a new pit will appear. The loop continues, and finally when begin<=end, the key value is placed in the last pit. Finally, the left side of the key is smaller than the key, and the right side is larger than the key, completing a single pass sorting.
//单趟排序 挖坑法
int PartSort1(int *arr,int left,int right)
{
int begin = left;
int end = right;
int index = GetMidindex(arr, left, right);
swap(&arr[begin], &arr[index]);
int key = arr[begin];
int pivot = begin;
//循环内实现左边小 右边大
while (begin < end)
{
while (begin < end && arr[end] >= key)
{
--end;
}
arr[pivot] = arr[end];
pivot = end;
while (begin < end && arr[begin] <= key)
{
++begin;
}
arr[pivot] = arr[begin];
pivot = begin;
}
pivot = begin;
arr[pivot] = key;
return pivot;
}
The second type: left and right pointer method
//左右指针法 细节缺陷较多
int PartSort2(int *arr, int left ,int right)
{
int begin = left;
int end = right;
int index = GetMidindex(arr, left, right);
swap(&arr[left], &arr[index]);
int keyi = begin;
while (begin < end)
{
//end找小 while (begin<end && arr[end]>=arr[keyi])
{
--end;
}
//begin找大
while (begin<end && arr[begin] <= arr[keyi])
{
++begin;
}
swap(&arr[begin], &arr[end]); //互换begin和end的值
}
swap(&arr[begin], &arr[keyi]);
return begin;
}
The third type: fast and slow pointers
Finally, when cur=n-1, swap prev and keyi to complete the single-pass sorting.
//单趟排序 快慢指针法方便好用!
int PartSort3(int*arr, int left, int right)
{
int prev = left;
int cur = left + 1;
int index = GetMidindex(arr, left, right);
swap(&arr[left], &arr[index]);
int keyi = left;
while (cur <= right)
{
//升序cur找比arr[keyi]小交换,降序cur找大交换
if (arr[cur] < arr[keyi])
{
++prev;
swap(&arr[prev], &arr[cur]); //通过prev使大的值往后扔
}
++cur;
}
swap(&arr[prev], &arr[keyi]);
return prev;
}
Divide and conquer recursion to achieve overall order
Finally, the overall sorting is implemented, using the idea of divide and conquer recursion. When the left subinterval is in order and the right subinterval is in order, the array is in order. Continue dividing the left and right subintervals, and finally when there is only one value in the interval, the subinterval is in order.
//递归实现
void QuickSort(int*arr, int left,int right)
{
if (left >= right) //区间只有一个值时
return;
int indexkey = PartSort3(arr, left, right);
//以下递归 为分治算法 满足key左区间有序 右区间有序,则该数组有序
QuickSort(arr, left, indexkey - 1);
QuickSort(arr, indexkey + 1, right);
}
Non-recursive algorithm achieves overall ordering (understand)
Simulate the stack frame in the operating system through the stack in the data structure
//非递归 实现快速排序
void QuickSortNonR(int*arr, int n)
{
Stack st;
StackInit(&st);
StackPush(&st,n-1); //将数组的左右区间 压入栈中
StackPush(&st, 0);
while (!StackEmpty(&st))
{
int left=StackTop(&st);
StackPop(&st);
int right=StackTop(&st);
StackPop(&st);
int indexkey = PartSort2(arr, left, right);
//如果被分子区间元素个数大于2 则继续入栈 出栈 操作
//[left,indexkey-1] [indexkey+1,right]
if (indexkey + 1 < right)
{
StackPush(&st, right);
StackPush(&st, indexkey+1);
}
if (left < indexkey - 1)
{
StackPush(&st,indexkey-1);
StackPush(&st, left);
}
}
StackDestory(&st);
}
merge sort
Code
//归并排序
void _MergeSort(int*arr, int left, int right, int*tmp)
{
if (left >= right)
{
return;//该子区间只有一个数时
}
//分治 对半分解数组
int mid = (left + right) >> 1;
_MergeSort(arr, left, mid, tmp);
_MergeSort(arr, mid + 1, right, tmp);
//按升序归并
int index = left;
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] < arr[begin2]) //选大的向临时数组中拷贝
{
tmp[index++] = arr[begin1++];
}
else
{
tmp[index++] = arr[begin2++];
}
}
while (begin1 <= end1)//当右区间归完,左区间还没归完时
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)//当左区间归完,右区间还没归完时
{
tmp[index++] = arr[begin2++];
}
//将临时数组的数据拷贝到原数组
for (int i = 0; i <= right; i++)
{
arr[i] = tmp[i];
}
}
void MergeSort(int*arr, int n)
{
int*tmp = (int*)malloc(sizeof(int)*n); //创建一个临时数组用来存放排好序的数
_MergeSort(arr,0, n-1, tmp);
free(tmp);
}