Detailed explanation of heap sort, quick sort, and merge sort algorithms

Table of contents

Heap sort

Heap concept

Implement heap sort

Time complexity of heap sort:

Quick sort

concept

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)

merge sort


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

Quick sort is an exchange sorting method with a binary tree structure proposed by Hoare in 1962. Its basic idea is: any selection to be sorted
A certain element in the element sequence is used as the reference value. According to the sorting code, the set to be sorted is divided into two subsequences. All the elements in the left subsequence
All elements are smaller than the baseline value, all elements in the right subsequence are greater than the baseline value, and then the process is repeated for the left and right subsequences until all
Until all elements are arranged in the corresponding position.
(Single pass sorting) There are three common ways to divide the interval into left and right halves according to the reference value:

The first type: digging method

 

First select a reference value (key) from the array, and set the position of the key value as the pit (pivot)

 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

 The idea is similar to the above method, but there is no need to set up a pivot to dig holes, and use exchange to achieve a smaller left side and a larger right side.
First select a subscript (keyi) of the reference value from the sequence. End first goes left to find a value smaller than the keyi position. Go right at begin to find a value larger than the keyi position, and then exchange begin and end. value, continue the loop, begin=end, and finally exchange keyi and begin to achieve the same effect.

 

//左右指针法  细节缺陷较多
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

Basic idea:
Merge sort ( MERGE-SORT ) is an effective sorting algorithm based on merge operations . The algorithm uses the divide-and-conquer method.
( Divide and Conquer ) A very typical application. Merge the ordered subsequences to obtain a completely ordered sequence
Column; that is, first order each subsequence, and then order the subsequence segments. If two ordered lists are merged into one ordered list, it is called two
The roads merge. Core steps of 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);
}

Summary of features of merge sort:
1. The disadvantage of merging is that it requires O(N) space complexity. The thinking of merging and sorting is more about solving the problem of external sorting on the disk.
question.
2. Time complexity: O(N*logN)
3. Space complexity: O(N)
4. Stability: Stable

Guess you like

Origin blog.csdn.net/LibraRhy/article/details/126195342