[Data structure and algorithm C++ implementation] 3. Sorting algorithm

The original video is Zuo Chengyun's B station teaching



For all the following swap() functions, the function is defined as

void swap(int& a, int& b)
{
    
    
	int t = a;
	a = b;
	b = t;
}
// 也可以用异或,但不能传入同一个变量,可以是不同变量相同值
void swap(int& a, int& b)
{
    
    
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

1 bubble sort O ( N 2 ) O(N^2)O ( N2 )(regardless of data status)

The most basic and simple sorting, nothing to say
Please add a picture description

#incldue <vector>
void bubbleSort(std::vector<int>& arr) 
{
    
    
    for (int i = 0; i < arr.size() - 1; i++) {
    
    
        for (int j = 0; j < arr.size() - 1 - i; j++) {
    
    
            if (arr[j] > arr[j+1]) {
    
            // 相邻元素两两对比
				swap(arr[j], arr[j+1]);
            }
        }
    }
}

2 selection sort O ( N 2 ) O(N^2)O ( N2 )(regardless of data status)

The basic idea is to select the smallest (or largest) element from the elements to be sorted each time and put it at the end of the sorted sequence. The main steps of selection sort are as follows:

  • 1. Traverse the sequence to be sorted, and set the current position as the position of the minimum value.
  • 2. Starting from the current position, compare the current element with the following elements in turn, find the smallest element, and record its position.
  • 3. Exchange the smallest element with the element at the current position.
  • 4. Repeat steps 2 and 3 until the entire sequence is traversed.
    Please add a picture description
#include<vector>
void selectionSort(std::vector<int>& arr)
{
    
    
	int len = arr.size();
	if (arr.empty() || len < 2) return;

	for (int i = 0; i < len -1; i++){
    
     // len -1 位置不用再循环了,因为最后剩一个必然是最值
		int minIndex = i;		// 在i ~ len-1 上寻找最小值的下标
		for (int j = i; j < len - 1; j++){
    
    
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
		}
		swap(arr[minIndex], arr[i]);
	}
}

3 Insertion sort O ( N 2 ) O(N^2)O ( N2 )(relative to data status)

The principle is similar to the process of sorting poker hands. Insert the unsorted elements one by one into the sorted part at the appropriate position.

The logical order is to first make 0~0 orderly, then 0~1 orderly, 0~2 ... 0~n-1 orderly

The algorithm flow is:

  • 1. Starting from the first element, the element can be considered sorted.
  • 2. Take the first value from the unsorted part and insert it into the appropriate position in the sorted part . The selection method of this position is: compare the current unsorted value with the left neighbor value, and exchange the position if it is smaller . It will end until it encounters the boundary or is not smaller than the value of the left neighbor. (Inner loop: find an appropriate position to insert)
  • 3. Repeat step 2 until all elements are inserted into the sorted sequence. (outer loop: traverse each unsorted number)

Unlike bubble sort and selection sort, this is a fixed operation (data status is irrelevant). In insertion sorting, if the order is given, then the outer loop will be completed every time, so it will be O(N) , but we say that the time complexity is worst case, so it is still O ( N 2 ) O (N^2)O ( N2)

Please add a picture description

#include <vector>
void insertionSort(std::vector<int>& arr)
{
    
    
	int len = arr.size();
	if (arr.empty() || len < 2) return;
	// 0~0已经有序了
	for (int i = 1; i < len; i++){
    
     	// 0~i做到有序
		// 把当前值(j+1)对比左邻值(j),比他小则交换,不满足循环条件则说明找到合适位置了
		for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--){
    
     
			swap(arr[j+1], arr[j]);
		}
	}
}

In the inner loop, we compare the current element arr[j + 1] with its left neighbor element arr[j], and transpose if it is smaller until a suitable insertion position is found.
The condition for the end of the loop (finding a suitable position) is: the left boundary is reached or the current value is greater than the value of the left neighbor

Continue to look down, it is recommended to master binary search and basic recursion first

4 Merge sort O ( N log N ) O(NlogN)O ( Nl o g N ) (data status does not matter)

Its core idea is to divide the sequence to be sorted into smaller subsequences until each subsequence has only one element , and then merge these subsequences in pairs until the entire sequence is finally sorted.

The following are the general steps of merge sort:

  • Segmentation : Split the sequence to be sorted into two subsequences from the middle position, and continue to recursively divide each subsequence until there is only one element left in each subsequence.
  • Merge : Merge two sorted subsequences into one sorted sequence. Start the comparison from the first elements of the two subsequences, put the smaller elements into the temporary array, and move the index of the corresponding subsequence backward by one bit until all the elements of one of the subsequences are put into the temporary array. Then put the remaining elements of the other subsequence directly into the temporary array.
  • Repeated merging : The merging operation is repeated until all subsequences are merged into an ordered sequence.

Always O(NlogN) time complexity, independent of the data. Although it is faster than the previous bubbling, selection, and insertion sorting, the space complexity is O(N)

The following figure describes this process very accurately.Please add a picture description

#include <vector>
// 第二阶段:merge阶段时,L~M 和 M~R 之间的数必然有序
void merge(std::vector<int>& arr, int L, int M, int R)
{
    
    
	std::vector help(R - L + 1);
	int i = 0;		// 临时数组的起始索引
	int p1 = L;		// 左半部分的起始索引
	int p2 = M + 1;	// 右半部分的起始索引

	//外排序,把两个子数组中的元素从小到大放到help数组
	while (p1 <= M && p2 <= R){
    
     
		help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
	}

 	// 如果左边部分还有剩的,全部依次加到help的末尾
	while (p1 <= M){
    
    
		help[i++] = arr[p1++];
	}
	// 同理如果右边还有剩的
	while (p2 <= R){
    
     
		help[i++] = arr[p2++];
	}

	// 结束,把临时数组的内容覆盖到原数组中
	for (i = 0; i < R - L + 1; i++){
    
    
		arr[L + i] = help[i]; // 注意arr从L位置开始写入的
	}
}

// 务必先看这个函数 这算是第一阶段拆分
void mergeSort(std::vector<int>& arr, int L, int R)
{
    
    
	if (L == R) return;	// 拆分完毕,不能再拆了
	
	int mid = L + ((R - L) >> 1);
	mergeSort(arr, L, mid);
	mergeSort(arr, mid+1, R);
	merge(arr, L, mid, R); // 执行到这一步的条件是 L == mid 且 mid+1 == R 即左右子树都已二分到只有一个元素
}

Time complexity (refer to 2.1 master formula )

  • T ( N ) = 2 ⋅ T ( N 2 ) + O ( N ) T(N) = 2·T(\frac{N}{2})+O(N) T(N)=2T(2N)+O ( N )
  • a = 2; b = 2; d = 1
  • l o g a b = 1 = d log_ab=1=d logab=1=d , so the time complexity isO ( N ⋅ log N ) O(N·logN)O(Nlog N ) _ _

Space Complexity: O ( N ) O(N)O ( N ) . Because every time a merge is opened up a space, the size is N

4.1 Extension of Merge Sort (Find the Small Sum of Arrays)

Topic: In an array, the sum of the numbers to the left of each number smaller than the current number is called the small sum of the array. Find the minimum sum of an array.

Example: [1,3,4,2,5]
The number on the left of 1 is smaller than 1, none; the
number on the left of 3 is smaller than 3, 1; the number
on the left of 4 is smaller than 4, 1, 3;
the number on the left of 2 is smaller than 2 The number, 1;
the number on the left of 5 smaller than 5, 1, 3, 4, 2;
so the small sum is 1+1+3+1+1+3+4+2=16
requiring time complexity O(NlogN) , space complexity O(N)

If the elements at each position go through all the elements on the left, find the number smaller than it, and sum them up, the result can be easily obtained, and the time complexity is O ( N 2 ) O(N^2)O ( N2)

Change thinking: instead of finding how many numbers on the left side are smaller than the number at the current position, and then summing, but counting how many numbers are greater than the number at the current position, how many times the current number should be accumulated, and this process is in It can be done in the outer sort of merge sort

Detailed explanation of the video and accurate airborne , much better than reading text

#include <iostream>
#include <vector>

long long mergeAndCount(std::vector<int>& arr, int left, int mid, int right) 
{
    
    
    std::vector<int> temp(right - left + 1); // 临时数组用于存储合并后的结果
    int i = 0; // 临时数组的起始索引
    int p1 = left; // 左半部分的起始索引
    int p2 = mid + 1; // 右半部分的起始索引
    long long count = 0; // 记录小和的累加和

    while (p1 <= mid && p2 <= right) {
    
    
        if (arr[p1] <= arr[p2]) {
    
    
            // 重点:如果arr[p1]比arr[p2]小,则arr[p1]比p2后面所有数都小!
            count += arr[p1] * (right - p2 + 1);
            temp[i++] = arr[p1++];
        } else {
    
    
            temp[i++] = arr[p2++];
        }
    }

    while (p1 <= mid) temp[i++] = arr[p1++];
    while (p2 <= right) temp[i++] = arr[p2++];

    // 将临时数组的结果复制回原数组
    for (i = 0; i < R - L + 1; i++) {
    
    
        arr[left + i] = temp[i];
    }

    return count;
}

long long mergeSortAndCount(std::vector<int>& arr, int left, int right) 
{
    
    
    if (left >= right) {
    
    
        return 0; // 单个元素无小和
    }

    int mid = left + (right - left) / 2;
    long long leftCount = mergeSortAndCount(arr, left, mid); // 左半部分的小和
    long long rightCount = mergeSortAndCount(arr, mid + 1, right); // 右半部分的小和
    long long mergeCount = mergeAndCount(arr, left, mid, right); // 合并过程中的小和

    return leftCount + rightCount + mergeCount; // 总的小和
}

long long calculateSmallSum(std::vector<int>& arr) 
{
    
    
    if (arr.empty()) return 0; // 空数组无小和

    int left = 0;
    int right = arr.size() - 1;
    return mergeSortAndCount(arr, left, right);
}

int main() 
{
    
    
    std::vector<int> arr = {
    
    3, 1, 4, 2, 5};
    long long smallSum = calculateSmallSum(arr);
    std::cout << "Small Sum: " << smallSum << std::endl;
	
	std::cin.get();
    return 0;
}

5 Quick Sort

Quicksort 3.0 (see elsewhere for 1.0 and 2.0)

Guess you like

Origin blog.csdn.net/Motarookie/article/details/131383596