[C language] classic_sort_algorithm

        Sorting is a lot in life. For example, when you buy things online, you need to sort according to conditions. It is also necessary to learn in programming learning. There are many ways to sort, but each has its own efficiency.


1. Insertion sort

// 插入排序
void InsertSort(int* a, int n)
{
	for (int i = 0; i < n;i++) {
		int end = i;
		int temp = a[end+1];

		while (end >= 0)
		{
			if (temp < a[end]) {
				a[end+1] = a[end];
				end--;
			}
			else {
				break;
			}
			a[end+1] = temp;
		}
	}
}

Ideas:

end is greater than end+1, overwrite the data corresponding to end, and move the end position backward.

Then continue to compare

 

At the end, insert the value inserted by end+1 into the head.

 

In this way, the sorting is completed once, and if you insert it multiple times, you will find that the array becomes an ascending array.

        

PS: The time complexity of insertion sort is O(N^2). But if the array is sorted or close to sorted, the efficiency will be much faster.

2. Hill sort

  Shell's Sort is a type of insertion sort, also known as "Diminishing Increment Sort", which is a more efficient and improved version of the direct insertion sort algorithm . Hill sort is an unstable sorting algorithm. This method is named after DLShell proposed in 1959.

Ideas:

        The main idea is that if the efficiency of insertion sorting is greatly improved when it is close to order, then it is possible to artificially create an orderly array.

A gap         is defined here , where the gap is defined as 3, and the array is divided into three groups, each with a distance of gap distances as a group, and these three groups are pre-sorted.

  

    int gap = 3;
	
	for (int j = 0; j < gap; j++) {

		for (int i = j; i < n - gap; i += gap) {

			int end = i;
			int temp = a[end+gap];

			while (end >= 0)
			{
				if (temp < a[end]) {
					a[end+gap] = a[end];
					end -= gap;
				}
				else {
					break;
				}
				a[end + gap] = temp;
			}
		}
	}

        You will find that the sorting of each group is insertion sorting. The code here can also be optimized. Here is a group-by-group sorting, resulting in three layers of loops. In fact, only two layers of loops are used.

    int gap = 3;

	for (int i = 0; i < n - gap; i++) {

		int end = i;
		int temp = a[end+gap];

		while (end >= 0)
		{
			if (temp < a[end]) {
				a[end+gap] = a[end];
				end -= gap;
			}
			else {
				break;
			}
			a[end + gap] = temp;
		}
	}	

In fact, an i ++         is changed here . The idea here is that instead of grouping for pre-sorting, it enters the next group after one group is sorted. The result is the same, and quite ingeniously.

        One pre-sorting has been completed here, and multiple pre-sortings are required here, so how to choose the gap? Generally, it is selected here to be equal to the size of the array at the beginning, and then slowly shrink until it is equal to 1.

  • Sort in ascending order, the larger the gap, the faster the larger numbers go to the back, the faster the smaller numbers go to the front, but the less orderly it is.
  • In ascending order, the smaller the gap, the closer to the order. When gap==1, it is insertion sort.
  • gap=n(array length)
  • gap=gap/3+1; (to be pre-sorted many times)
  • When gap>1 is and sorting, the last gap equal to 1 is direct insertion sorting.

code:

void ShellSort(int* a, int n)
{
	int gap = n;

	while(gap > 1){

        gap = gap/3+1;

        for (int i = 0; i < n - gap; i++){

            int end = i;
            int temp = a[end+gap];

            while (end >= 0)
            {
                if (temp < a[end]) {
                    a[end+gap] = a[end];
                    end -= gap;
                }
                else {
                    break;
                }

                a[end + gap] = temp;
            }
        }	
	}
}

test:

 time complexity:

        O(N^1.3)

+1 can be ignored when calculating the gap, gap/3/3/3/3/3/...=N, then the pre-sort is log3N.

Inside the for loop is 2/3*N.

It is mainly how to calculate in the while loop. The mathematical calculation is more complicated, and the main idea is to explain it.

1. When the gap is large, it is about 2N times.

        When the gap is large, the jump is very fast.

2. When the gap is very small, it is probably 2N times.

        When the gap is small, it has been pre-sorted before, so the array is close to order, so the loop goes very fast.

 It is N*log3N (note that this is a calculation, not accurate, just remember O(N^1.3)).

3. Heap sort

// 堆排序  建小堆
void AdjustDwon(int* a, int n, int parent)
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child]) { 
            //注意child+1<size要在前面判断,不然越界
			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) 
{
	for (int i = (n - 1 - 1) / 2; i >= 0;i--) {  //注意这里是i>=0
		AdjustDwon(a, n, i);
	}

	int size = n - 1;
	while (size>0) {
		swap(&a[0],&a[size]);
		AdjustDwon(a,size,0);//注意这里使用的size
		size--;
	}
}

        It has been mentioned before, so I won’t go into details. 

Heap introduction: https://blog.csdn.net/weixin_45423515/article/details/124916001?spm=1001.2014.3001.5501

Fourth, bubble sort

// 冒泡排序
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n-1;i++) {
		for (int j = 0; j < n-i-1;j++) {

			if (a[j+1]<a[j]) {
				swap(&a[j+1],&a[j]);
			}

		}
	}
}

        Very easy-to-understand sorting, and also the first sorting to learn. The idea is also very simple, just exchange the next one and the previous one if the number is large.

time complexity:

       O(N^2) 

Five, selection sort

// 选择排序
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin<end) {

		int max = begin, min = begin;
		for (int i = begin ; i <= end;i++) { //这里只有4个数的时候begin+1是错误的

			if (a[i] < a[min])
				min = i;
			if (a[i] > a[max])
				max = i;
		}
		swap(&a[begin], &a[min]);

		if (begin ==  max) {
			max = min;
		}

		swap(&a[end],&a[max]);

		begin++;
		end--;
	}
}

        The idea is also relatively simple. When you encounter a small number, put it in front, and put a large number in the back, which consumes a lot of efficiency, and unlike insertion sort, if the array is close to order, it will improve efficiency. Regardless of whether the array is ordered or not, the time is complicated The degree is worst O(N^2).

PS: It should be noted here that if begin is the largest number, the largest number will be replaced after the exchange here, so it needs to be judged here.

time complexity:

        O(N^2) 

Six, quick sort (recursive method)

        

// 快速排序递归实现
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end) {
		return;
	}

	int  left = begin , right = end ;
	int key = left;

	while (left < right ) {
        //注意找的是纯小于key的数,所以要用<=,>=
        //为了防止一直找不到,要加上left<right防止越界

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

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

		

		swap(&a[left] , &a[right]); //找到后交换
	}

	swap(&a[key],&a[left]);

	QuickSort(a,begin,left - 1);
 
	QuickSort(a,left + 1,end);
}

Ideas:

        Let the leftmost number be used as a key (the rightmost one is also acceptable), and then let the rest of the array compare the keys. If it is larger than the key, put it on the right side, and if it is smaller than the key, put it on the left side.

        The two sides look for each other and exchange after finding it. Find the end (when left and right meet) and exchange with the key, so that the key is in the most suitable position.

        Then, with the idea of ​​​​divide and conquer, let the two sides be sorted separately.

Note where to start looking:

        It should be noted here that if the key selects the left side, then the right side must start to walk (the right side is selected, and the left side starts to walk) . The reason is to ensure that the value of the meeting position is smaller than the key value. The value that stops from the right is always a value smaller than the key, so it can be exchanged with the key.

        If you start from the left, assuming that all you encounter are values ​​smaller than the key, when you encounter right at the end, the position is just larger than the key, and then exchange, you will find that the large value is changed to the beginning, which is wrong.

 

 

        If you start from the right, assuming that all you encounter are values ​​larger than the key, right will find a number smaller than the key until it meets left. The key is large, and the position at the beginning is the most suitable position. (Why do you have to start from the right? There are many examples, you can just remember this simpler example)

test:

Guess you like

Origin blog.csdn.net/weixin_45423515/article/details/125133131
Recommended