zoukankan      html  css  js  c++  java
  • 小小c#算法题

    快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题。

    快速排序(Quick Sort)是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(数据结构,严蔚敏)。

    更具体一点的说,首先我们从待排序列中选取一个元素作为支点(pivot),这个支点的选择可以任意选取,所以经常直接拿第一个元素作为支点,更好一点的办法是三者取中法,即取第一个元素,最后一个元素,中间一个元素,这三个值中间的中值(非最大最小)作为支点。支点选好之后,把支点放到待排序序列的第一个位置,如果你选的是第一个元素作为支点的话,不用做什么,如果是其他情况,那么就把选好的支点跟第一个元素交换一下。然后通过代码将所有比支点小的记录都放在它之前的位置,将所有比它大的记录都放到它的位置之后。由此最后可以以该支点的位置作分界线,将序列分割成两个子序列。这个过程我们称为一趟快速排序(或一次划分)。

    一次快速排序的具体实现:

    将支点放在序列的第一个位置,最左边。然后从高位下标high(初始值为最后一个元素的位置)开始往前搜索,找到第一个小于支点的元素,将其和支点元素交换,即把一个比支点小的元素放在了支点的位置,而支点现在所处的新位置之后的元素都是比其大的,因为这个位置是我们从高位找到第一个比支点小的元素的原先位置;然后从低位low(初始值为第一个元素的位置)开始往后搜索,找到第一个大于支点的记录,将其和支点元素交换,由于前面的交换中支点所处的位置之后的元素都比其大,通过这一次的交换的话,这个较大的元素现在所在的位置往后的元素都是大于支点的,而且,支点回到的新的位置之前的元素都是比支点小的。重复这两步,直到从低位搜索和从高位搜索的两个index(也可以理解为指针)相遇,即low ==  high。

    其实,上面提到的每次交换元素的操作可以精简一下,对支点记录的赋值是多余的,因为只有在一趟排序结束时,才能确定支点的最后位置。所以有一个临时变量将支点的值记录下来就可以了。每次交换时,只把需要变换位置的元素放到新的位置就可以了。

    下面是代码,这段代码采用第一个元素作为支点,如果没有用第一个元素作为支点的话,那么交换一下,把支点放到第一个位置即可,然后采用下面相同的代码:

            static int Partition(int[] numbers, int low, int high)
            {
                int pivot = numbers[low];
                while (low < high)
                {
                    while (low < high && numbers[high] >= pivot)
                    {
                        high--;
                    }
    
                    numbers[low] = numbers[high];
    
                    while (low < high && numbers[low] <= pivot)
                    {
                        low++;
                    }
                    numbers[high] = numbers[low];
                }
    
                numbers[low] = pivot;
                return low;
            }


    上面是一次快速排序的代码,最后返回的是支点的位置。下面就是对支点左右两边的序列分别进行快速排序,当子序列只有两个元素时,再通过一次排序该子序列就是有序的了,由此实现整个序列有序。可见,这是一个递归的操作。只有当序列长度大于1时才进行快速排序,因为只有一个元素的序列肯定是有序的,所以跳出递归的条件是low<high。只有一个元素时,low 是等于 high的。

            static void QuickSort(int[] numbers, int low, int high)
            {
                if (low < high)
                {
                    int partitionLocation = Partition(numbers, low, high);
                    QuickSort(numbers, low, partitionLocation - 1);
                    QuickSort(numbers, partitionLocation + 1, high);
                }
            }

    调用的过程就如:

            static void Main(string[] args)
            {
                int[] numbers = { 49, 38, 65, 97, 76, 13, 27, 49 };
                QuickSort(numbers, 0, numbers.Length - 1);
            }

    就平均时间而言,快速排序是目前认为是最好的一种内部排序方法。(数据结构,严蔚敏)。

    最后,快速排序的平均时间复杂度是O(nlogn),最坏情况是O(n2),如序列本身就有序时,将蜕变为冒泡排序。空间复杂度为O(logn),这是因为递归过程要维护一个栈。

    如果不好理解的话,最好的办法就是对着代码,手动走几遍,或者找些有配图的书或文章看看。

  • 相关阅读:
    LeetCode 79. 单词搜索
    LeetCode 1143. 最长公共子序列
    LeetCode 55. 跳跃游戏
    LeetCode 48. 旋转图像
    LeetCode 93. 复原 IP 地址
    LeetCode 456. 132模式
    LeetCode 341. 扁平化嵌套列表迭代器
    LeetCode 73. 矩阵置零
    LeetCode 47. 全排列 II
    LeetCode 46. 全排列
  • 原文地址:https://www.cnblogs.com/CSharpSPF/p/3175388.html
Copyright © 2011-2022 走看看