数据结构之快速排序

1.快速排序是对冒泡排序的一种改进,是所有内部排序算法中平均性能最优的排序算法

首先我们要先找一个基准值4,让在4左边的都是小于4的数,在4右边的都是大于4的数。比如下面arr数组中索引在l到j之间的都是小于4的,索引在j+1到i-1之间都是大于4的。 

如果想要让整个数组都变成有序的,那就需要进行递归,对各个区间中的数据分别做快速排序,直到数据有序。

 

 

 

 代码实现:

  class QuickSort
    {
        public static void Sort(int[] arr)
        {
            Sort(arr, 0, arr.Length - 1);
        }

        private static void Sort(int[] arr, int l, int r)
        {
            if (r - l + 1 <= 15)
            {
                //也可以直接返回,或者使用直接插入排序
                InsertSort(arr, l, r);
                return;
            }

            //基准值
            int v = arr[l];
            int j = l;
            for (int i = l + 1; i <= r; i++)
            {
                //如果当前值小于基准值v,要先j++,,然后交换,确保基准值左侧都是小于它的数;
                if (arr[i] < v)
                {
                    j++;
                    Swap(arr, i, j);

                }
            }

            //将基准值放到符合左右2侧分别小于大于它的地方
            Swap(arr, l, j);
            //使用递归分别对基准值左右2侧的数据进行快速排序
            Sort(arr, l, j - 1);//左侧
            Sort(arr, j + 1, r);//右侧
        }
        /// <summary>
        /// 元素交换
        /// </summary>
        /// <param name="arr"></param>
        /// <param name="i"></param>
        /// <param name="j"></param>
        private static void Swap(int[] arr, int i, int j)
        {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
        /// <summary>
        /// 插入排序 升序
        /// </summary>
        /// <param name="arr"></param>
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                //插入元素e
                int e = arr[i];
                //j表示元素e应该插入的位置
                int j;
                for (j = i; j > 0; j--)
                {
                    if (e < arr[j])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        //此时说明不用再比较了
                        break;
                    }
                }
                //通过for循环判断,最终找到了j具体的位置,将元素e放在此位置上
                arr[j] = e;
            }


        }

    }

 

 

随机化快速排序

但是当数组处于一种近乎有序的情况下,便会非常慢,因为之前我们都是默认第一个元素为基准元素 

代码更改(具体):

  class QuickSort
    {
        private static Random random;


        public static void Sort(int[] arr)
        {
            Sort(arr, 0, arr.Length - 1);
        }

        private static void Sort(int[] arr, int l, int r)
        {
            if (r - l + 1 <= 15)
            {
                //也可以直接返回,或者使用直接插入排序
                InsertSort(arr, l, r);
                return;
            }

            //基准值
            int p = l + random.Next(r - l + 1);//随机的基准值索引
            Swap(arr, l, p);
            int v = arr[l];
            int j = l;
            for (int i = l + 1; i <= r; i++)
            {
                //如果当前值小于基准值v,要先j++,,然后交换,确保基准值左侧都是小于它的数;
                if (arr[i] < v)
                {
                    j++;
                    Swap(arr, i, j);

                }
            }

            //将基准值放到符合左右2侧分别小于大于它的地方
            Swap(arr, l, j);
            //使用递归分别对基准值左右2侧的数据进行快速排序
            Sort(arr, l, j - 1);//左侧
            Sort(arr, j + 1, r);//右侧
        }
        /// <summary>
        /// 元素交换
        /// </summary>
        /// <param name="arr"></param>
        /// <param name="i"></param>
        /// <param name="j"></param>
        private static void Swap(int[] arr, int i, int j)
        {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
        /// <summary>
        /// 插入排序 升序
        /// </summary>
        /// <param name="arr"></param>
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                //插入元素e
                int e = arr[i];
                //j表示元素e应该插入的位置
                int j;
                for (j = i; j > 0; j--)
                {
                    if (e < arr[j])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        //此时说明不用再比较了
                        break;
                    }
                }
                //通过for循环判断,最终找到了j具体的位置,将元素e放在此位置上
                arr[j] = e;
            }


        }

    }

 

三向切分的快速排序方法

还有一种情况是存在大量重复元素,这也会导致性能降低,甚至栈溢出。所以我们可以通过三向切分的方法来解决,将数组分为三部分:小于当前切分元素的部分,等于当前切分元素的部分,大于当前切分元素的部分。如下用不同颜色区分的展示情况:

 

 

 

代码实现:

  /// <summary>
    /// 三向切分的快速排序类
    /// </summary>
    class QuickSort3
    {
        private static Random random;


        public static void Sort(int[] arr)
        {
            Sort(arr, 0, arr.Length - 1);
        }

        private static void Sort(int[] arr, int l, int r)
        {
            //(1)如果数组量不大,可以直接用插入排序
            if (r - l + 1 <= 15)
            {
                //也可以直接返回,或者使用直接插入排序
                InsertSort(arr, l, r);
                return;
            }

            //基准值(2)防止出现近乎有序的数组,使用随机索引值
            int p = l + random.Next(r - l + 1);//随机的基准值索引
            Swap(arr, l, p);
            int v = arr[l];
            int lt = l;//当前数组区域的开始索引
            int gt = r + 1;//当前数组区域的最大索引值加1
            int i = l + 1;//定义i,用它指向遍历的每一个元素
            while (i < gt)
            {
                if (arr[i] < v)
                {
                    //之所以要先递增lt,是因为现在lt指向的数据是
                    //默认比基准值小的数,所以要前进一步,否则更换就没意义了。
                    lt++;
                    Swap(arr, i, lt);
                    i++;
                }
                else if (arr[i] > v)
                {
                    gt--;
                    Swap(arr, i, gt);
                }
                else
                {
                    i++;
                }
            }

            //此时要
            Swap(arr, l, lt);
            //使用递归进行快速排序
            Sort(arr, l, lt - 1);//左侧
            Sort(arr, gt, r);//右侧
        }
        /// <summary>
        /// 元素交换
        /// </summary>
        /// <param name="arr"></param>
        /// <param name="i"></param>
        /// <param name="j"></param>
        private static void Swap(int[] arr, int i, int j)
        {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
        /// <summary>
        /// 插入排序 升序
        /// </summary>
        /// <param name="arr"></param>
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                //插入元素e
                int e = arr[i];
                //j表示元素e应该插入的位置
                int j;
                for (j = i; j > 0; j--)
                {
                    if (e < arr[j])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        //此时说明不用再比较了
                        break;
                    }
                }
                //通过for循环判断,最终找到了j具体的位置,将元素e放在此位置上
                arr[j] = e;
            }


        }

    }

 

 

总结:

设置一个基准值,这个基准值最好是随机的,然后将整个无序表分成3个区间,一个区间是存放小于基准值的,一个是等于基准值,一个是大于基准值的,然后再对各个区间做快速排序处理,知道整个数组有序

posted @ 2021-09-05 10:44  安静点--  阅读(477)  评论(0编辑  收藏  举报