關於快速排序算法(一個90%的人都不懂其原理、99.9%的人都不能正常寫出來的算法.)


一、奇怪的現象

  研究快速排序很久了,發現一個古怪的實情:這算法描述起來很簡單,寫一個正確的出來實在不容易.寫一個優秀的快速排序算法更是難上加難.

也難怪該算法提出來過了很久才有人寫出一個正確的算法,過了很久才優秀的版本出來.

二、原理描述

  1. 從數列中挑出一個元素,稱為 "基准"(pivot),
  2. 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作。
  3. 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序.

三、最容易讓人理解的版本

一個List形式的數組,找到其中的第一個做基准,遍歷剩下的數據,然后創建兩個左右List,大的放右邊,小的放左邊.然后對左右List各自進行如上操作.......

詳情見代碼

​using  System;
using  System.Collections.Generic;

using  System.Text;
namespace test
{
    class Program
    {
        static void Main(string[]args)
        {
            List<int> array=new List<int>();
            array.AddRange(new int[]{3,2,9,5,8,6,23,23,222,12,21});
            var t1=DateTime.Now.Ticks;
            sort(array);
            var t2=DateTime.Now.Ticks;
            Console.WriteLine(t2-t1);
            
            Console.ReadLine();
        }
        static int Count=0;
        public static void sort(List<int> array)
        {
            var guid=Count++;
            Console.WriteLine();
            Console.Write("目標字符串:("+guid+")");
            Show(array);
            
            if (array.Count<=1) {
                return;
            }
            if (array.Count==2) {
                if (array[0]>array[1]) {
                    var temp=array[0];
                    array[0]=array[1];
                    array[1]=temp;
                    Console.Write("雙值交換:");
                    Show(array);
                }
            }
            else{
                var xData=array[0];
                var leftList=new List<int>();
                var rightList=new List<int>();
                for (int i = 1; i < array.Count; i++) {
                    var t=array[i];
                    if (t<=xData) {
                        leftList.Add(t);
                    }else{
                        rightList.Add(t);
                    }
                }
                
                Console.WriteLine("中間值:"+xData);
                
                Console.Write("左邊的字符串("+guid+"):");
                Show(leftList);
                
                Console.Write("右邊的字符串("+guid+"):");
                Show(rightList);
                
                sort(leftList);
                Console.Write("左邊的字符串(排序后)("+guid+"):");
                Show(leftList);
                
                sort(rightList);
                Console.Write("右邊的字符串(排序后)("+guid+"):");
                Show(rightList);
                
                array.Clear();
                array.AddRange(leftList);
                array.Add(xData);
                array.AddRange(rightList);
                Console.Write("排好的("+guid+"):");
                Show(array);
                
                Console.WriteLine();
                leftList.Clear();
                rightList.Clear();
            }
        }
        public static void Show(List<int>array){
            foreach (var a in array) {
                Console.Write(a+",");
            }
            
            Console.WriteLine();
        }
    }
}

 四、正確但不優化的版本

using System;
namespace Sort
{
    class Program
    {
        static void Main(string[] args)
        {
            string result = string.Empty;
            int[] unsort = {  2, 0, 3, 7, 5,6 };
            //快速排序
            QuickSort(unsort, 0, unsort.Length - 1);
            Console.ReadLine();
        }

        /// <summary>
        /// 調用快速排序算法
        /// </summary>
        /// <param name="unsort">待排序的整形數組</param>
        /// <param name="left">左邊起始點</param>
        /// <param name="right">右邊結束點</param>
        public static void QuickSort(int[] unsort, int left, int right)
        {
            if (left < right)
            {
                //獲取一次排序的中間索引位置
                int midPosition = GetSplitNum(unsort, left, right);
                
                //遞歸實現
                QuickSort(unsort, left, midPosition - 1);
                QuickSort(unsort, midPosition + 1, right);
            }
        }
         
        static int ORDER_INDEX=1;
        /// <summary>
        /// 獲取一次排序的中間索引位置
        /// </summary>
        /// <param name="unsort">待排序的整形數組</param>
        /// <param name="left">左邊起始點</param>
        /// <param name="right">右邊結束點</param>
        public static int GetSplitNum(int[] unsort, int left, int right)
        {
            int splitNum = unsort[left];
            while (left < right)
            {
                /**
                 * 從右端開始比較
                 * (1)假如從右端過來的數比分隔數要大,則不用處理
                 * (2)假如從右端過來的數比分隔數要小,則需要挪到分隔線左邊
                 * */
                while (left < right && splitNum <= unsort[right])
                {
                    right--;
                }
                unsort[left] = unsort[right];
                GetPrint(unsort);
                /**
                 * 從從端開始比較
                 * (1)假如從左端過來的數比分隔數要小,則不用處理
                 * (2)假如從左端過來的數比分隔數要大,則需要挪到分隔線右邊
                 * */
                while (left < right && splitNum >= unsort[left])
                {
                    left++;
                }
                unsort[right] = unsort[left];
                GetPrint(unsort);
            }
            //一趟比較之后,分隔數的位置就可以確認起來
            unsort[left] = splitNum;
            Console.WriteLine(string.Format("第{0}輪排序完畢",(ORDER_INDEX++)));
            GetPrint(unsort);
            return left;
        }

        /// <summary>
        /// 打印輸出結果
        /// </summary>
        /// <param name="unsort">數據</param>
        public static string GetPrint(int[] unsort)
        {
            string result = string.Empty;
            foreach (int n in unsort)
            {
                if (!string.IsNullOrEmpty(result))
                {
                    result += string.Format("->{0}", n);
                }
                else
                {
                    result = string.Format("{0}", n);
                }
            }
            Console.WriteLine(result);
            return result;
        }
        public static string GetPrint(int[] unsort,int replaceIndex)
        {
            string result = string.Empty;
            foreach (int n in unsort)
            {
                if (!string.IsNullOrEmpty(result))
                {
                    result += string.Format("->{0}", n);
                }
                else
                {
                    result = string.Format("{0}", n);
                }
            }
            Console.WriteLine(result+"   ==>"+replaceIndex);
            return result;
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM