[Data structure] Take you to play sorting: heap sorting, Hill sorting, insertion sorting, selection sorting, bubble sorting, quick sorting (multiple versions), merge sorting

 5a2585dded9b416fb4ea58637b42ed39.png

  Yan-yingjie's homepage

Awareness of the past, not remonstrance, knowing the future, can be pursued  

C++ programmer, 2024 electronic information graduate student


Table of contents

Implementation of Common Algorithms

        insertion sort

        Hill sort

        heap sort

        selection sort

        Bubble Sort

        quick sort

        Hoare version

        Randomly select Keyi      

        Take the middle of three

        pit digging

        Front and back pointer version

        merge sort


Implementation of Common Algorithms

        insertion sort

                Animation demo:

        Ideas (in ascending order):

                From the very beginning, we take the first digit and the second digit and compare them. If the first digit is greater than the second digit,

Then exchange the first digit with the second digit. If it is less than, jump out directly. At this time, an exchange is completed, and end continues to

Move forward and compare continuously. As i changes, the position of comparison also changes

        code:

void InsertSort(int* a,int n)
{
	for (int i=1; i < n; i++)
	{
		int end = i-1;
		int temp = a[i];

		while (end >= 0)
		{
			if (a[end] > temp)
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = temp;
	}
}
Summary of the characteristics of direct insertion sort:
1. The closer the element set is to order, the higher the time efficiency of the direct insertion sorting algorithm
2. Time complexity: O(N^2)
3. Space complexity: O(1), it is a stable sorting algorithm
4. Stability: Stable

        Hill sort

                Picture demo:

                Ideas (ascending order)

                        We compare the array every gap number, and compare the current number with the number after the gap

If the current number is greater than the number after the gap number, it will be exchanged, if not, then jump out of the loop, when the outer loop is in progress

++ operation, start from the second number, compare with the number after the gap digits from the second number, and it is greater than the intersection

Change, less than jumping out of the loop, while the gap is constantly changing, and finally when gap==1, the final comparison is made

        

                code:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap>1)
	{
		gap =gap/ 2;
		for (int i = 0; i< n-gap;i++)
		{
			int end = i;
			int temp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > temp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = temp;
		}
	}
}
Summary of the characteristics of Hill sorting:
        1. Hill sort is an optimization of direct insertion sort.
        2. When gap > 1, it is pre-sorted, the purpose is to make the array closer to order. When gap == 1, the array is already close to order, so it will be very fast. In this way, the overall optimization effect can be achieved. After we implement it, we can compare performance tests.
        3. The time complexity of Hill sorting is not easy to calculate, because there are many ways to value the gap, which makes it difficult to calculate, so the time complexity of Hill sorting given in many trees is not fixed

       

        heap sort

         Heapsort (Heapsort) refers to a sorting algorithm designed using a stacked tree (heap) data structure, which is a type of selection sort. It selects data through the heap. It should be noted that you need to build a large heap for ascending order, and a small heap for descending order.

                Ideas:

                First of all, a large heap should be built, and the heap cannot be used directly. The array that needs to be sorted can be regarded as a heap, but the array structure needs to be turned into a heap. We can start from the rightmost second line of the heap from bottom to top and adjust it down until it is adjusted to the top of the heap, so that the array can be It is adjusted into a heap, and if a large heap is created, the top element of the heap is the maximum value. Then exchange the data at the top and bottom of the heap according to the idea of ​​heap deletion, but the difference is that the last element is not deleted here. In this way, the largest element is at the last position, and then adjusted from the top of the heap to the penultimate element, so that the next largest element is at the top of the heap, and the above steps are repeated until only the top of the heap is left.
 

                Picture demo:

       

                

// 堆排序
void AdjustDown(int* a, int n, int root)//向下调整
{
	assert(a);
	int parent = root;
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
 
void HeapSort(int* a, int n)
{
	assert(a);
 
    //建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
 
    //交换
	for (int i = n - 1; i > 0; i--)
	{
		Swap(&a[i], &a[0]);
		AdjustDown(a, i, 0);
	}
}

       

        selection sort

        Ideas (in ascending order):

                For the first time, select the smallest (or largest) element from the data elements to be sorted and store it at the beginning (end) of the sequence, then select the second smallest (or second largest) element and store it in the largest ( The next position of the smallest) element, repeat this step until all the data elements to be sorted are exhausted.

               code:

void SelectSort(int* arr, int n)
{
	//保存参与单趟排序的第一个数和最后一个数的下标
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		//保存最大值的下标
		int maxi = begin;
		//保存最小值的下标
		int mini = begin;
		//找出最大值和最小值的下标
		for (int i = begin; i <= end; ++i)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		//最小值放在序列开头
		Swap(&arr[mini], &arr[begin]);
		//防止最大的数在begin位置被换走
		if (begin == maxi)
		{
			maxi = mini;
		}
		//最大值放在序列结尾
		Swap(&arr[maxi], &arr[end]);
		++begin;
		--end;
	}
}
        A summary of the characteristics of selection sort:
1. Direct selection sort thinking is very easy to understand, but the efficiency is not very good. rarely used in practice
2. Time complexity: O(N^2)
3. Space complexity: O(1)
4. Stability: Unstable

        

        Bubble Sort

                Algorithm thinking:

                Compare from left to right, in ascending order: if the left is greater than the right, exchange, from to to the end, all exchange

Change, select the largest element and put it at the end. This is a single-pass sorting, and then loop to select the second largest element, and then loop to select the first

Big three

                Code:

                

//交换
void Swap(int* a,int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//冒泡实现
void BubbleSort(int* a,int n)
{
	for (int j =0; j<n; j++)
	{
		for (int i = 1; i < n-j; i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
			}
		}
	}

}
         Summary of the characteristics of bubble sort:
         1. Bubble sort is a very easy-to-understand sort
        2. Time complexity: O(N^2)
        3. Space complexity: O(1)
        4. Stability: Stable

       

        quick sort

                In 1962, a binary tree structure exchange sorting method proposed by Hoare, its basic idea: any selection to be sorted

A certain element in the element sequence is used as the reference value, and the set to be sorted is divided into two subsequences according to the sort code, and the left subsequence

All elements are less than the reference value, all elements in the right subsequence are greater than the reference value, and then the leftmost subsequence repeats the process

process until all elements are arranged in the corresponding position .

                Select a key value key and put it in the correct position. Before we learn any sorting, we should first understand the single

Sorting, and then clear the overall idea

         Hoare version

                         The left side is the key and the right side is the first, if the right side is the key, the left side is the first, change the small one to the left, and the big one to the right

        Change to the meeting position and stop. At this time, the left side is smaller than the middle position, and the right side is larger than the middle position. The meeting position

        Must be smaller than Key

                 Hoare's sorting is actually arranging a data             

void QuickSort(int* a,int left ,int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left, end = right;

	int keyi = left;
	while (left < right)
	{
		//右边先走找小
		while (a[right] >= a[keyi] && left < right)
		{
			--right;
		}
		
		//左边找大
		while (a[left] <= a[keyi] && left < right)
		{
			++left;
		}
		Swap(&a[left],&a[right]);
	}
	Swap(&a[keyi],&a[left]);
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

        Note: But it has a fatal feature, if the keyi is on the far left, and the array is in order , when sorting, you need to keep

Create a new stack frame, its time complexity is directly n*logn, we can randomly arrange the position of Keyi

        Randomly select Keyi      

                We can solve the above problem by randomly processing keyi

void QuickSort(int* a, int left, int right)
{
	//  ... 返回条件
	if (left >= right)
	{
		return;
	}
	int begin = left, end = right;
	int randi = left + (rand() % (right - left));
	Swap(&a[left],&a[randi]);
	int keyi = left;
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= a[keyi])
			--right;

		// 左边找大
		while (left < right && a[left] <= a[keyi])
			++left;

		Swap(&a[left], &a[right]);
	}

	Swap(&a[keyi], &a[left]);
	keyi = left;

	// [begin, keyi-1] keyi [keyi+1, end] 
	// 递归
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

        Take the middle of three

                In the case of order, the optimization of the time complexity of taking the middle of three numbers is extremely obvious

void QuickSort(int* a, int left, int right)
{
	//  ... 返回条件
	if (left >= right)
	{
		return;
	}
	int begin = left, end = right;
	int midi = GetMidNumi(a,left,right);
	Swap(&a[midi],&a[left]);
	/*int randi = left + (rand() % (right - left));
	Swap(&a[left],&a[randi]);*/

	int keyi = left;
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= a[keyi])
			--right;

		// 左边找大
		while (left < right && a[left] <= a[keyi])
			++left;

		Swap(&a[left], &a[right]);
	}

	Swap(&a[keyi], &a[left]);
	keyi = left;

	// [begin, keyi-1] keyi [keyi+1, end] 
	// 递归
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

        pit digging

        

int QuickSort(int* a, int left, int right)
{
	//  ... 返回条件
	if (left >= right)
	{
		return;
	}
	int begin = left, end = right;
	int midi = GetMidNumi(a,left,right);
	if(midi != left)
		Swap(&a[midi],&a[left]);
	
	int key = a[left];
	int hole = key;
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= key)
			--right;
		a[hole] = a[right];
		hole = right;

		// 左边找大
		while (left < right && a[left] <= key)
			++left;
		a[hole] = a[left];
		hole = left;

	}
	a[hole] = key;

	// [begin, keyi-1] keyi [keyi+1, end] 
	// 递归
}
void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int keyi = QuickSort(a,left,right);
	QuickSort1(a,left,keyi-1);
	QuickSort1(a,keyi+1,right);
}

        Front and back pointer version

                1. Cur finds a value smaller than key, ++prev, value exchange between cur and prev, ++cur

                2.cur finds a value larger than key, ++cur

                 illustrate

                        1.prev or followed by cur

                         2. There is a value interval between prev and cur that is larger than the key

        Ideas:

        By creating two pointers, the prev pointer points to the position of the first element of the array sequence, and cur points to the next position of prev. Cur traverses to find a value smaller than the key reference value. If found, it depends on whether the next position of prev is cur The position, if the next position of prev is indeed the position of cur, then exchange the value pointed to by cur with the value pointed to by prev, if the next position of prev is not the position of cur, then cur continues to move forward Traverse until the end of cur traversal, and finally exchange the key reference value with the value pointed to by prev, and finally confirm that the reference value is in the middle of the array sequence.

//交换函数
void Swap(int* p1, int* p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//三数取中
int GetMidIndex(int* arr, int left, int right) {
	int mid = (right - left) / 2 + left;
	if (arr[left] < arr[mid]) {
		if (arr[mid] < arr[right]) {
			return mid;
		}
		else if (arr[left] > arr[right]) {
			return left;
		}
		else {
			return right;
		}
	}
	else {			//arr[left]>=arr[mid]
		if (arr[mid] > arr[right]) {
			return mid;
		}
		else if (arr[left] < arr[right]) {
			return left;
		}
		else {
			return right;
		}
	}
}
 
//前后指针法
int PartSort(int* arr, int left, int right) {
	int mid = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[mid]);
	int keyi = left;	//基准值下标放在最左侧
	int prev = left;
	int cur = left + 1;
	while (cur<=right) {
		if (arr[cur] < arr[keyi] && ++prev != cur) {
			Swap(&arr[prev], &arr[cur]);
		}
		++cur;
	}
	Swap(&arr[keyi], &arr[prev]);	
	return prev;
}

        merge sort

        

        Ideas:

        Merge sort from bottom to top: Divide the sequence to be sorted into several sub-sequences of length 1, and then merge these sequences in pairs; obtain several ordered sequences of length 2, and then merge these sequences in pairs; Get a number of ordered sequences with a length of 4, and then merge them two by two; until they are directly merged into one sequence, merge and sort from top to bottom: it is in the opposite direction to "bottom to top" in sorting.

     code:  

        

//辅助归并排序递归的子函数
void _MergeSort(int* a, int* tmp, int begin, int end)
{
	if (begin >= end)
		return;//单个或者不存在其区间就结束递归
 
	//类似于后序:
	int middle = (begin + end) / 2;
	int begin1 = begin;
	int end1 = middle;
	int begin2 = middle + 1;
	int end2 = end;
	_MergeSort(a, tmp, begin1, end1);
	_MergeSort(a, tmp, begin2, end2);
 
	//此时认为 [begin1, end1] 和 [begin2, end2]两段区间上有序
	//归并算法
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
 
}
 
//归并排序 递归版本
void MergeSort(int* a, int n)
{
	//首先malloc一个数组
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("未能申请到内存\n");
		exit(-1);
	}
	//第一次传入 0 和 n - 1 传入闭区间
	_MergeSort(a, tmp, 0, n - 1);
	free(tmp);
}

Send book at the end of the article:

        

 Book Recommendations:

        An algorithm is not a line of mysterious code, but a thinking mode that you and I should have. Algorithms existed before computers existed. Understanding the origin of classic algorithms is not only because we are in the information age, but also because the underlying logic and application of algorithms can provide new ideas for solving problems in our work and life

 

        This book starts with the most basic discussion of "what is an algorithm", first introduces how to evaluate the performance of an algorithm, then discusses classic algorithms related to graphs, search and sorting, explains "how the algorithm works", and finally introduces PageRank and depth Learn two large algorithmic applications. This book uses easy-to-understand language to describe the algorithm world, interspersed with interesting cultural and historical stories and simple and easy-to-understand examples. It does not involve difficult mathematical knowledge, and even non-professionals can easily understand it.

About the Author:

        Panos Louridas

        PhD in software engineering from the University of Manchester. He is currently an associate professor at the Department of Management Science and Technology, Athens University of Economics and Business. His research interests include algorithm applications, software engineering, security and practical cryptography, etc. Author of Real-World Algorithms: A Beginner's Guide. Before joining the university, he worked as a senior software engineer in an investment bank.

Guess you like

Origin blog.csdn.net/m0_73367097/article/details/130863080