十大排序算法

排序算法

基于比较的排序算法

插入排序(insertion sort)

类似抓牌,每次将未排序的第一个元素放入排好序的序列中,从排好序的子序列中,从尾依次向前遍历,如果大于待插入的元素,则往后移一个位置,否则找到放入的位置

void InsertSort(vector<int>& vec) {
    size_t nvec = vec.size();
    int i, j;
    for (i = 1; i < nvec; i++) {
        int tmp = vec[i]; //待排序的元素
        for (j = i - 1; j >= 0; j--) {
            if (vec[j] <= tmp) {
                break;
            }
            vec[j + 1] = vec[j]; //比当前元素大,往后移动一个位置
        }
        vec[j + 1] = tmp; //找到位置,把当前元素放入
    }
}

可使用二分搜索插入,加快查询速度。

希尔排序(shell sort)

希尔排序是插入排序的变体,其核心思想在于尽快的把小元素挪到大元素前面,对原始数组按照步长进行分组,步长递减,对每个分组采用插入排序,代码如下

void ShellSort::operator()(std::vector<int>& vec) {
    int j;
    for (int step = vec.size() / 2; step > 0; step /= 2) {
        for (int i = step; i < vec.size(); i++) {
            int temp = vec[i];
            for (j = i - step; j >= 0; j -= step) {
                if (vec[j] <= temp) {
                    break;
                }
                vec[j + step] = vec[j];
            }
            vec[j + step] = temp;
        }
    }
}

选择排序(selection sort)

每次遍历未排序的序列,找到最小的元素,放到排好序的最后一个位置。

void SelectioSort(vector<int>& vec) {
    size_t nvec = vec.size();
    int min_index = 0;
    for (int i = 0; i < nvec - 1; i++) {
        min_index = i;
        for (int j = i + 1; j < nvec; j++) {
            if (vec[j] < vec[min_index]) {
                min_index = j;
            }
        }
        std::swap(vec[i], vec[min_index]);
    }
}

冒泡排序(bubble sort)

两两相邻比较,将最大值往右边推进

void BubbleSort(vector<int>& vec) {
    size_t nvec = vec.size();
    for (size_t i = nvec; i > 1; i--) {
        for (size_t j = 0; j < i - 1; j++) {
            if (vec[j] > vec[j + 1]) {
                std:swap(vec[j], vec[j + 1]);
            }
        }
    }
}

快速排序(quic sort)

有两种实现,一种是基于交换,另外一种是基于挖坑

挖坑的实现

void QuicSort(vector<int>& vec, int start, int end) {
    if (start >= end) {
        return;
    }
    int head = start;
    int tail = end;
    int pivot = vec[end];
    while(start < end) {
        while(start < end && vec[start] <= pivot) {
            start++;
        }
        vec[end] = vec[start];
        while(start < end && vec[end] > pivot) {
            end--;
        }
        vec[start] = vec[end];
    }
    vec[start] = pivot;
    QuicSort(vec, head, start - 1);
    QuicSort(vec, start + 1, tail);
}

快排的另一种实现

int Partition(vector<int>& vec, int left, int right) {
    int current_index = left - 1;
    int pivot = vec[right];
    while(left < right) {
        if (vec[left] <= pivot) {
            ++current_index;
            int temp = vec[left];
            vec[left] = vec[current_index];
            vec[current_index] = temp;
        }
        left++;
    }
    int temp = vec[current_index + 1];
    vec[current_index + 1] = vec[right];
    vec[right] = temp;
    return current_index + 1;
}

void QuickSort(vector<int>& vec, int left, int right) {
    if (left >= right) {
        return;
    }
    int medium = Partition(vec, left, right);
    QuickSort(vec, left, medium - 1);
    QuickSort(vec, medium + 1, right);
}

该方法易于理解,即把比pivot小的元素移至左边。并最后把pivot值放在左边的最后的一个位置。

相关题目:

1.leetcode 215. 数组中的第K个最大元素

归并排序(merge sort)

该算法类似于快速排序,采用分治法,将原数组划分成两半,然后对两半递归调用,接着合并排好序的两部分。整个过程如下图所示

请添加图片描述

参考代码如下

void Merge(std::vector<int>& vec, int left, int mid, int right) {
    int nsize = right - left + 1;
    std::vector<int> temp(nsize, 0);
    int left_index = left;
    int right_index = mid + 1;
    int current_index = 0;
    while(left_index <= mid && right_index <= right) {
        if (vec[left_index] <= vec[right_index]) {
            temp[current_index++] = vec[left_index++];
        } else {
            temp[current_index++] = vec[right_index++];
        }
    }
    while(left_index <= mid) {
        temp[current_index++] = vec[left_index++];
    }
    while(right_index <= right) {
        temp[current_index++] = vec[right_index++];
    }

    for (int i = 0; i < nsize; i++) {
        vec[left + i] = temp[i];
    }
}

void MergeSort(std::vector<int>& vec, int left, int right) {
    if (left >= right) {
        return;
    }
    int mid = left + (right - left) / 2;
    MergeSort(vec, left, mid);
    MergeSort(vec, mid + 1, right);
    Merge(vec, left, mid, right);
}

相关题目

1.leetcode 493. 翻转对

桶排序(bucket sort)

桶排序适用于已知待排元素取值范围的场景,从数学视角看,其核心思想为找到一个通项

e i = a ⋅ k + b ,    i ∈ [ 0 , n − 1 ] e_i=a\cdot k+ b, \ \ i\in[0,n-1] ei=ak+b,  i[0,n1]

把取值在区间 ( a ⋅ ( k − 1 ) + b ,   a ⋅ k + b ] (a\cdot (k-1) + b,\ a\cdot k+b] (a(k1)+b, ak+b] 的元素 e x e_x ex放到索引为 k k k的桶(bucket)上面。

具体过程为:

  1. 找到最大值元素和最小值元素记为 a m a x a_{max} amax a m i n a_{min} amin,并计算取值范围 a m a x − a m i n a_{max}-a_{min} amaxamin
  2. 计算切割取值范围的单元长度 u = a m a x − a m i n n u = \frac{a_{max}-a_{min}}{n} u=namaxamin。(n为元素个数)
  3. 取出元素 e i e_i ei,计算其对应的bucket索引 i n d e x = a i − a m i n u index=\frac{a_i-a_{min}}{u} index=uaiamin

实际上,上述通项公式可以写为s

e i = u ⋅ k + a m i n = a m a x − a m i n n ⋅ k + a m i n e_i=u\cdot k+a_{min}=\frac{a_{max}-a_{min}}{n}\cdot k+a_{min} ei=uk+amin=namaxamink+amin

因此可以推导出计算桶索引 k k k的公式

k = e i − a m i n u k=\frac{e_i-a_{min}}{u} k=ueiamin

对每个bucket上的元素可采用插入排序。

对应代码如下

void BucketSort(std::vector<int>& vec) {
    int nsize = vec.size();
    if (!nsize) {
        return;
    }
    int max_value = *max_element(vec.begin(), vec.end());
    int min_value = *min_element(vec.begin(), vec.end());
    int unit = ceil(double(max_value - min_value) / (double)nsize);
    if (!unit) { // all values are equal
        return;
    }
    std::vector<int> buckets[nsize + 1];
    for (int i = 0; i < nsize; i++) {
        int bucket_index = (vec[i] - min_value) / unit;
        assert(bucket_index >= 0 && bucket_index <= nsize);
        buckets[bucket_index].push_back(vec[i]);
    }

    for (int i = 0; i <= nsize; i++) {
        std::sort(buckets[i].begin(), buckets[i].end());
    }

    int current_index = 0;
    for (int i = 0; i <= nsize; i++) {
        for (int j = 0; j < buckets[i].size(); j++) {
            vec[current_index++] = buckets[i][j];
        }
    }
}

堆排序(heap sort)

Applications of Heaps:
1)
Heap Sort: Heap Sort uses Binary Heap to sort an array in O(nLogn) time.
2) Priority Queue: Priority queues can be efficiently implemented using Binary Heap because it supports insert(), delete() and extractmax(), decreaseKey() operations in O(logn) time. Binomoial Heap and Fibonacci Heap are variations of Binary Heap. These variations perform union also efficiently.
3) Graph Algorithms: The priority queues are especially used in Graph Algorithms like Dijkstra’s Shortest Path and Prim’s Minimum Spanning Tree.
4) Many problems can be efficiently solved using Heaps. See following for example.a) K’th Largest Element in an array.b) **Sort an almost sorted array/**c) Merge K Sorted Arrays.

对于二项堆,当使用数组表示时,对于当前节点索引 i i i,其父节点下标为 ⌊ i − 1 2 ⌋ \lfloor \frac{i-1}{2} \rfloor 2i1,其左孩子节点下标为 2 ⋅ i + 1 2\cdot i + 1 2i+1,其右孩子节点下标为 2 ⋅ i + 2 2\cdot i + 2 2i+2

堆排序分为两个步骤

  1. 建堆
  2. 不断取出堆顶元素,并重新堆化
void Heapify(vector<int>& vec, int root_index, int current_size) {
    int max_index = root_index;
    while (root_index < current_size) {
        int left_index = root_index * 2 + 1;
        int right_index = root_index * 2 + 2;
        if (left_index < current_size && vec[left_index] > vec[max_index]) {
            max_index = left_index;
        }
        if (right_index < current_size && vec[right_index] > vec[max_index]) {
            max_index = right_index;
        }
        if (root_index != max_index) {
            int temp = vec[max_index];
            vec[max_index] = vec[root_index];
            vec[root_index] = temp;
            root_index = max_index;
        } else {
            return;
        }
    }
    return;
}

void BuildHeap(vector<int>& vec) {
    current_size_ = vec.size();
    int last_leaf_node_index = vec.size() - 1;
    int last_non_leaf_node_index = (last_leaf_node_index - 1) / 2;
    for (int i = last_non_leaf_node_index; i >= 0; i--) {
        Heapify(vec, i, vec.size());
    }
}

void HeapSort(vector<int>& vec) {
    BuildHeap(vec);
    int current_size = vec.size();
    while (current_size > 0) {
        int last_node_index = current_size - 1;
        int temp = vec[0];
        vec[0] = vec[last_node_index];
        vec[last_node_index] = temp;
        current_size--;
        Heapify(vec, 0, current_size);
    }
}

能堆化的前提是 只有堆顶部分不满足最大/最小结构

Many problems can be efficiently solved using Heaps. See following for example.
a) K’th Largest Element in an array
b) Sort an almost sorted array/
c) Merge K Sorted Arrays

许多问题可以使用堆来解决,比如

  1. 找到数组里的第k大元素 leetcode 215题
  2. 对于一个几乎有序的数组排序
  3. 合并k个有序子数组 leetcode 23题

计数排序(counting sort)

此排序方法必须保证元素集合是可数且有限的。且当元素个数与值域范围相近才能体现优势,排序过程如下

For simplicity, consider the data in the range 0 to 9. 
Input data: 1, 4, 1, 2, 7, 5, 2
  1) Take a count array to store the count of each unique object.
  Index:     0  1  2  3  4  5  6  7  8  9
  Count:     0  2  2  0   1  1  0  1  0  0

  2) Modify the count array such that each element at each index 
  stores the sum of previous counts. 
  Index:     0  1  2  3  4  5  6  7  8  9
  Count:     0  2  4  4  5  6  6  7  7  7

The modified count array indicates the position of each object in 
the output sequence.

  3) Rotate the array clockwise for one time.
   Index:     0 1 2 3 4 5 6 7 8 9
   Count:     0 0 2 4 4 5 6 6 7 7
  
  4) Output each object from the input sequence followed by 
  increasing its count by 1.
  Process the input data: 1, 4, 1, 2, 7, 5, 2. Position of 1 is 0.
  Put data 1 at index 0 in output. Increase count by 1 to place 
  next data 1 at an index 1 greater than this index.

其必须满足通过 O ( 1 ) O(1) O(1)的时间找到元素最终所在的位置范围。

代码如下

void CountSort(std::vector<int>& vec) {
    int nsize = vec.size();
    if (!nsize) {
        return;
    }
    int max_value = *max_element(vec.begin(), vec.end());
    int min_value = *min_element(vec.begin(), vec.end());
    int shift = min_value;
    std::vector<int> count(max_value - shift + 1, 0);
    for (int i = 0; i < nsize; i++) {
        count[vec[i] - shift]++;
    }

    for (int i = 1; i < count.size(); i++) {
        count[i] += count[i - 1];
    }

    std::vector<int> temp(nsize, 0);
    for (int i = 0; i < nsize; i++) {
        int last_index_of_current = count[vec[i] - shift] - 1;
        temp[last_index_of_current] = vec[i];
        count[vec[i] - shift]--;
    }

    for(int i = 0; i < nsize; i++) {
        vec[i] = temp[i];
    }
}

基数排序(radix sort)

基数排序是计数排序的扩展,对于待排元素,在进制 k k k下表示的数值中,每个数字(digit)的取值范围为 [ 0 , k − 1 ] [0,k-1] [0,k1]之间,因此从最低位开始,对每个digit采用计数排序(counting sort),到最大值的最高位结束。记最大值为 e m a x e_{max} emax,其空间复杂度为 O ( k ) \mathcal{O}(k) O(k),时间复杂度为 O ( ⋅ log ⁡ k e m a x ( n + k ) ) \mathcal{O}(\cdot \log_k^{e_{max}}(n+k)) O(logkemax(n+k)),对应代码如下:

void RadixSort::CountSort(std::vector<int>& vec, int base) {
    int nsize = vec.size();
    if (!nsize) {
        return;
    }

    int shift = *min_element(vec.begin(), vec.end());

    std::vector<int> count(10, 0);
    for (int i = 0; i < nsize; i++) {
        int digit = ((vec[i] - shift) / base) % 10;
        count[digit]++;
    }

    for (int i = 1; i < 10; i++) {
        count[i] += count[i - 1];
    }

    std::vector<int> temp(nsize, 0);
    for (int i = nsize - 1; i >= 0; i--) {
        int digit = ((vec[i] - shift) / base) % 10;
        int last_index_of_current = count[digit] - 1;
        assert(last_index_of_current >= 0 && last_index_of_current < nsize);
        temp[last_index_of_current] = vec[i];
        count[digit]--;
    }
    for (int i = 0; i < nsize; i++) {
        vec[i] = temp[i];
    }
}

void RadixSort::operator()(std::vector<int>& vec) {
    int max_value = *max_element(vec.begin(), vec.end());
    for (int base = 1; max_value / base > 0; base *= 10) {
        this->CountSort(vec, base);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值