排序之交换排序(冒泡排序、快速排序)

冒泡排序

阐述:

通过比较相邻两个元素的值,将最小值(假设从小到大排序)置换到待排集合的首位,通俗说就是把最小值放到待排集合的前面。

冒排包括两个遍历,确定待排集合的首位置,遍历待排集合置换出最小值,所以时间复杂度是O(N*N)。

冒排的是两个相邻元素的位置交换,相邻操作很显然不会打乱相同元素的相对位置,故而是稳定的。

冒排关键字:临近元素两两比较。

效果图:

 

代码(c#):

        /// <summary>
        /// 冒泡排序
        /// </summary>
        public static void DoBubblingSort()
        {
            List<int> listNum = new List<int>() { 3, 10, 11, 2, 6, 4, 2, 17, 19 };
            LogWrite.LogPlain(DisplayList(listNum));

            for (int i = 0; i < listNum.Count - 1; i++) //索引 i 用于定位待排集合的首位置
            {
                for (int j = listNum.Count - 1; j > i; j--) //遍历待排集合,通过临近的元素两两交换,把最小值置换到待排集合的首位
                {
                    if (listNum[j - 1] > listNum[j])
                    {
                        //交换临近元素的值
                        Swap(listNum, j - 1, j);
                        //LogWrite.LogPlain(DisplayList(listNum, new List<int> { j - 1, j }));
                    }
                }
            }
        }

        /// <summary>
        /// 交换数组两个元素的值
        /// </summary>
        /// <param name="listNum"></param>
        /// <param name="indexSource"></param>
        /// <param name="indexTarget"></param>
        public static void Swap(List<int> listNum, int index1, int index2)
        {
            int swapTemp = listNum[index1];
            listNum[index1] = listNum[index2];
            listNum[index2] = swapTemp;
        }

 

快速排序:

阐述:

快排通过取中值获得一个基准数(获得基准的方法还有其他),通过一趟排序使得基准的左边元素小于基准值,基准右边的元素大于基准值,然后对由基准分开的两个部分继续执行上述操作,递归到处理对象集合数为1停止(递归操作有点像深度优先一样,一定要走到处理对象集合数为1才会停止递归)。

同样是交换元素位置获得有序的集合,快排比起冒排效率高多了,根源还是来自神奇的递归操作,递归操作是分治思想的实现方式之一,递归的过程就是遍历二叉树的过程,而树的每一层操作相对独立、隔离,比起冒排会大大减少冗余的比较,所以效率相对很高。

快排在每一层的处理中,在置换基准时,可能使得相同元素的相对位置发生变化,索引快排是不稳定的。

快排关键字:基准,递归。

时间复杂度:

快排的基准,如果经过一趟排序都能落在集合的中间位置,这是最好的情况了,因为这样下去递归的轨迹会呈现出一个枝叶丰满的二叉树(满二叉树或者完全二叉树),然后树深度到达一个理想值lgN,树深度越大,需要递归的次数就越多,很显然性能就会下降,另外,树的每一层置换的时间复杂度是O(N),所以在最好的情况下快排的时间复杂度是O(N*lgN)。

最不幸运的情况下,快排的基准经过一趟排序落在集合的边缘位置,这样下去递归的轨迹会呈现出一个很单调的二叉树(专业点说就是退化成一个没有分支的二叉树,树高变成了N),也就是需要进行N趟递归才能停止,复杂度就是O(N),所以在最坏的情况下快排的时间复杂度是O(N*N)。

树的深度由lgN退化到了N,这是呈指数级的退化啊,还是很可观的退化。

算法描述:

1.取中值作为基准,使得基准跑到数组合适的位置(左边的小于基准值,右边的大于基准值)
2.处理基准的左部(若有),重新走第一步,直到集合数为1
3.处理基准的右部(若有),重新走第一步,直到集合数为1
4.结束

效果图:

 代码(c#):

     /// <summary>
        ///  快速排序方法入口
        /// </summary>
        public static void DoQuickSort_Entrance()
        {
            List<int> listNum = new List<int>() { 3, 10, 19, 20, 6, 4, 22, 17, 19 };
            DoQuickSort_Sort(listNum, 0, listNum.Count - 1);
        }

        /// <summary>
        /// 快速排序实现
        /// </summary>
        public static void DoQuickSort_Sort(List<int> listNum, int indexLeft, int indexRight)
        {
            //检查索引
            if (!(indexLeft > -1 && indexRight < listNum.Count && indexRight >= indexLeft))
                return;
            
            int start = indexLeft;
            int end = indexRight;
            int pivotKey = listNum[(indexLeft + indexRight) / 2];//通过取中值获取基准数
            //LogWrite.LogPlain(DisplayList(listNum, new List<int>() { (indexLeft + indexRight) / 2,indexLeft,indexRight }, '<', '>'));

            //左索引和右索引向基准遍历
            while (indexLeft <= indexRight)
            {
                //获取左半部比基准大的元素
                while (listNum[indexLeft] < pivotKey && indexLeft < end) { indexLeft++; }
                //获取右半部比基准小的元素
                while (listNum[indexRight] > pivotKey && indexRight > start) { indexRight--; }; 

                if (indexLeft <= indexRight) 
                {
                    //交换上面两个元素的相对位置   
                    Swap(listNum, indexLeft, indexRight);
                    //LogWrite.LogPlain(DisplayList(listNum, new List<int>() { indexLeft, indexRight }));
                    //使得左右索引继续向基准逼近
                    indexLeft++;
                    indexRight--;
                }
            }

            //基准将集合一分为二,分别递归处理左半部分和右半部分
            if (start < indexRight)
                DoQuickSort_Sort(listNum, start, indexRight);
            if (indexLeft < end)
                DoQuickSort_Sort(listNum, indexLeft, end);
        }

      /// <summary>
        /// 交换数组两个元素的值
        /// </summary>
        /// <param name="listNum"></param>
        /// <param name="indexSource"></param>
        /// <param name="indexTarget"></param>
        public static void Swap(List<int> listNum, int index1, int index2)
        {
            int swapTemp = listNum[index1];
            listNum[index1] = listNum[index2];
            listNum[index2] = swapTemp;
        }

 

 

posted on 2013-02-20 11:16  豆沙包没有肉  阅读(323)  评论(0编辑  收藏  举报

导航