代码改变世界

排序算法

2010-07-02 16:20  Clingingboy  阅读(1188)  评论(0编辑  收藏  举报

 

一.冒泡排序(左右两两比较)

 

排序过程(比较次数恒定不变)

  • 将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序r[1].key>r[2].key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1个记录和第n个记录比较为止——第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上
  • 对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置
  • 重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止
//ascend sort
       public static void BubbleSort(this int[] arr)
       {
           int outer, inner;

           //outer loop
           for (outer= arr.Length-1; outer>0; outer--)
           {
               Console.Write(arr[arr.Length - outer - 1] + ": ");
               arr.Display();
               //inner loop
               for (inner = 0; inner < outer; inner++)
               {
                   //compare left and right value
                   if (arr[inner] > arr[inner + 1])
                   {
                       //swap
                       int temp = arr[inner];
                       Console.Write(string.Format(" {0} swap with {1}  ", arr[inner], arr[inner + 1]));
                       arr[inner] = arr[inner + 1];
                       arr[inner + 1] = temp;
                   }
                   else
                   {
                       Console.Write(string.Format(" {0} don't need swap  ", arr[inner]));
                   }
                   arr.Display();
               } 
           }
       }

二.选择(选择最大的交换)排序

选择排序是冒泡排序的改进版,减少了交换次数,(每次只找出最小值或者最大值,然后与最左侧或者最右侧交换),全是交换最大值,那么必定有一方便变成有序了

  • 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换
  • 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换
  • 重复上述操作,共进行n-1趟排序后,排序结束

缺点就是不管是否排好序,比较次数还是恒定不变

public static void SelectSort(this int[] arr)
{
    int outer, inner,max;

    //outer loop
    for (outer = arr.Length - 1; outer > 0; outer--)
    {
        max = outer;
        Console.Write(arr[outer] + ": ");
        arr.Display();
        //inner loop
        for (inner = 0; inner < outer; inner++)
        {
            //compare inner with min value
            if (arr[inner] >arr[max])
            {
                //mark max
                max = inner;
            }
        }
        //swap
        int temp = arr[outer];
        Console.Write(string.Format(" {0} swap with {1}  ", arr[outer], arr[max]));
        arr[outer] = arr[max];
        arr[max] = temp;
        arr.Display();
    }
}

三.插入(将一个值插入到局部有序的数组)排序

排序过程:

  • 整个排序过程为n-1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序

在我们打牌的时候,首先牌是乱序的,然后我们会慢慢的一张一张的把牌理顺,这便是一个插入排序过程,插入排序假定左侧是有序的,然后在右侧取值,再在左侧中比较插入值,如果有比出现右侧值大的,则把从该项开始的右侧全部向右移位,接着再给该项赋值...这个描述实在是...只能供自己理解了

public static void InsertionSort(this int[] arr)
      {
          int outer, inner;

          //outer loop
          for (outer = 1; outer <arr.Length; outer++)
          {
              arr.Display();
              //current sort value
              int temp = arr[outer];
              inner = outer;
              //move range right
              while (inner > 0 && arr[inner - 1] >= temp)
              {
                  arr[inner] = arr[inner-1];
                  --inner;
              }
              arr[inner] = temp;
              Console.Write(string.Format(" {0} swap with {1}  ", arr[outer], temp));
            
              arr.Display();
          }
      }

局部有序=>右移数组(打牌就没有这个步骤了)=>插入值,插入排序减少了比较次数,但交换次数增加了

四.归并排序(合并数组+递归)

即将两个数组合并成一个有序的数组

1.前提要将两个数组先排序(arr1和arr2已经排序)

public static int[] MergeSort(int[] arr1, int[] arr2)
       {
           var arr1Length = arr1.Length;
           var arr2Length = arr2.Length;
           var arr1Index = 0;
           var arr2Index = 0;
           var arr = new int[arr1Length + arr2Length];
           for (int i = 0; i < arr.Length; i++)
           {
               if (arr1Index == arr1Length)
               {
                   arr[i] = arr2[arr2Index++];
                   continue;
               }
               if (arr2Index == arr2Length)
               {
                   arr[i] = arr1[arr1Index++];
                   continue;
               }
               if (arr1[arr1Index] < arr2[arr2Index])
                   arr[i] = arr1[arr1Index++];
               else
                   arr[i] = arr2[arr2Index++];
           }
           
           return arr;
       }

2.按上面思想,将一个数组分为两个数组,然后一直递归的切分,直到分为两个或者一个,那么就可以直接对两个值或者一个值进行排序,接着将两个有序的数组合并.

即先切成多份数组,然后左右两两合并.
需要一个外部变量作为存储,搞了我好久…

        public static void recMergeSort(long[] workSpace, int lowerBound,
                                            int upperBound)
        {
            if (lowerBound == upperBound)            // if range is 1,
                return;                              // no use sorting
            else
            {                                    // find midpoint
                int mid = (lowerBound + upperBound) / 2;
                // sort low half
                recMergeSort(workSpace, lowerBound, mid);
                // sort high half
                recMergeSort(workSpace, mid + 1, upperBound);
                // merge them
                merge(workSpace, lowerBound, mid + 1, upperBound);
            }  // end else
        }  // end recMergeSort()
   //-----------------------------------------------------------
           public static void merge(long[] workSpace, int lowPtr,
                           int highPtr, int upperBound)
           {
               int j = 0;                             // workspace index
               int lowerBound = lowPtr;
               int mid = highPtr - 1;
               int n = upperBound - lowerBound + 1;       // # of items

               while (lowPtr <= mid && highPtr <= upperBound)
                   if (theArray2[lowPtr] < theArray2[highPtr])
                       workSpace[j++] = theArray2[lowPtr++];
                   else
                       workSpace[j++] = theArray2[highPtr++];

               while (lowPtr <= mid)
                   workSpace[j++] = theArray2[lowPtr++];

               while (highPtr <= upperBound)
                   workSpace[j++] = theArray2[highPtr++];

               for (j = 0; j < n; j++)
                   theArray2[lowerBound + j] = workSpace[j];

           }  // end merge()

五.希尔排序

希尔排序是插入排序的改进版本.其通过增加间隔数来减少交换次数

        public static void ShellSort(this int[] theArray)
        {
            int inner, outer;
            int temp;
            theArray.Display();
            int h = 1;                     // find initial value of h
            while (h <= theArray.Length / 3)
                h = h * 3 + 1;                // (1, 4, 13, 40, 121, ...)

            while (h > 0)                     // decreasing h, until h=1
            {
                // h-sort the file
                for (outer = h; outer < theArray.Length; outer++)
                {
                    temp = theArray[outer];
                    inner = outer;
                    // one subpass (eg 0, 4, 8)
                    while (inner > h - 1 && theArray[inner - h] >= temp)
                    {
                        theArray[inner] = theArray[inner - h];
                        inner -= h;
                    }
                    theArray[inner] = temp;
                    theArray.Display();
                }  // end for
                h = (h - 1) / 3;              // decrease h
            }  // end while(h>0)
          //  theArray.Display();
        }  // end shellSort()

实际数组是先两两排序,然后再进行三个排序,不然序列并不会顺序

六.划分算法

即以一个数为基数,把大于和小于此数的数字列为一组
如{2,1,4,5,10,6,7}以7为关键字,则变为{2,1,4,5,6,10,7}

原理是在左右两侧定位比关键字小或大的数字,然后两者交换,当定位点相同时,则退出循环

public static int Partition(this int[] theArray, int pivot)
{
    var left = 0;
    var right = theArray.Length - 1;
    int leftPtr = left - 1;           // right of first elem
    int rightPtr = right + 1;         // left of pivot
    while (true)
    {
        while (leftPtr < right &&       // find bigger item
              theArray[++leftPtr] < pivot)
            ;  // (nop)

        while (rightPtr > left &&       // find smaller item
              theArray[--rightPtr] > pivot)
            ;  // (nop)
        if (leftPtr >= rightPtr)        // if pointers cross,
            break;                      //    partition done
        else                           // not crossed, so
            Swap(theArray,leftPtr, rightPtr);    //    swap elements
    }  // end while(true)
    return leftPtr;                   // return partition

}

七.快速排序

快速排序基于划分算法,并递归执行划分算法,当一次划分算法执行好后,然后对两组数组再次执行划分算法,到最后只剩两个数字的时候就比较出来了.
(归并排序先要拆分,但最后却要花时间合并)

public static void QuickSort(this int[] theArray, int left, int right)
        {
            if (right - left <= 0)              // if size <= 1,
                return;                      //    already sorted
            else                             // size is 2 or larger
            {
                var pivot = theArray[right];      // rightmost item
                // partition range
                int partition = Partition(theArray,left, right, pivot);
                QuickSort(theArray, left, partition - 1);   // sort left side
                QuickSort(theArray,partition + 1, right);  // sort right side
            }
        }
public static int Partition(this int[] theArray,int left,int right, int pivot)
{
    int leftPtr = left - 1;           // right of first elem
    int rightPtr = right;         // left of pivot
    while (true)
    {
        while (       // find bigger item
              theArray[++leftPtr] < pivot)
            ;  // (nop)

        while (rightPtr > 0 &&       // find smaller item
              theArray[--rightPtr] > pivot)
            ;  // (nop)
        if (leftPtr >= rightPtr)        // if pointers cross,
            break;                      //    partition done
        else                           // not crossed, so
            Swap(theArray, leftPtr, rightPtr);    //    swap elements
    }  // end while(true)
    Swap(theArray, leftPtr, right);
    return leftPtr;                   // return partition

}

八.折半插入排序

对插入排序的改进
(1)插入排序的比较值是从有序表的左侧开始比较 比如1,2,3,5,8,-1 那么对于插入-1的排序则比较有利
(2)从有序表的中间开始比较 比如1,2,3,5,8,4 那么对于插入4的排序则比较有利
主要还是要看本身数据的乱序程度
理论是不够的,网上代码与自己的写的还是有些不同,不然自己思想就被束缚住了.还是得自己亲手来写.

public static void BInsertionSort(this int[] arr)
      {
          int outer, inner;

          //outer loop
          for (outer = 1; outer <arr.Length; outer++)
          {
              arr.Display();
              //current sort value
              int temp = arr[outer];
              int low = 0, high = outer;

              //compute current positon ready for insert
              while (low <= high)
              {
                  var mid = (low + high) / 2;
                  if (temp > arr[mid])
                      low = mid + 1;
                  else high = mid - 1;
              }
              //11,5
              //low high
              //move range right
              for (int j = outer-1; j >high; j--)
              {
                  arr[j + 1] = arr[j];
              }
              //set current position value
              arr[high + 1] = temp;
              arr.Display();
          }
      }

九.基排序(桶排序)

如数字198,以10为基数,那么可以分为1,9,8
基排序没有数字交换,而是在内部按组存储基数

image

(1)先取数组中的最大值,如702
(2)取最大值位数,702的位数是3
(3)根据位数的长度,然后以基数为运算
(4)第一次排序以个位数进行排序(忽略十位和百位),那么数字是有序的
(5)第二次排序根据第一次的数组为基础进行十位排序(忽略百位),那么数字是有序的,由于个位是有序的,那么十位有序,数字必定有序
(6)第三次根据第二次的数组为基础进行百位排序(忽略百位和个位),那么数字是有序的.由于个位和十位有序,数字必定有序

//排序
private static void RadixSort(ref int[] arr, int iMaxLength)
{
    List<int> list = new List<int>();//存放每次排序后的元素
    List<int>[] listArr = new List<int>[10];//十个桶
    char currnetChar;//存放当前的字符 比如说 某个元素123 中的2
    string currentItem;//存放当前的元素 比如说 某个元素123
    for (int i = 0; i < listArr.Length; i++)//给十个桶分配内存初始化。
        listArr[i] = new List<int>();
    for (int i = 0; i < iMaxLength; i++)//一共执行iMaxLength次,iMaxLength是元素的最大位数。
    {
        foreach (int number in arr)//分桶
        {
            currentItem = number.ToString();//将当前元素转化成字符串
            try {
                currnetChar = currentItem[currentItem.Length - i - 1];
            }//从个位向高位开始分桶
            catch {
                listArr[0].Add(number);
                continue;
            }
            //如果发生异常,则将该数压入listArr[0]。; break;
            //比如说5 是没有十位数的,执行上面的操作肯定会发生越界异常的,这正是期望的行为,我们认为5的十位数是0,所以将它压入listArr[0]的桶里。
            var index = Convert.ToInt32(currnetChar.ToString());
            listArr[index].Add(number);
        }
        for (int j = 0; j < listArr.Length; j++)//将十个桶里的数据重新排列,压入list
            foreach (int number in listArr[j].ToArray<int>())
            {
                list.Add(number);
                listArr[j].Clear();//清空每个桶
            }
        arr = list.ToArray<int>();//arr指向重新排列的元素
        //Console.Write("{0} times:",i);
        Print(arr);//输出一次排列的结果
        list.Clear();//清空list
    }
}

参考:http://baike.baidu.com/view/1170573.htm?fr=ala0_1_1

十.堆排序

1.堆是一个以数组形式实现的并且每个节点都比其子节点大的完全二叉树

(1)插入操作
比较简单,在最后节点插入一个数值,然后与父节点比较,若遇值小者则交换
向上找值

        public void trickleUp(int index)
        {
            int parent = (index - 1) / 2;
            Node bottom = heapArray[index];

            while (index > 0 &&
                   heapArray[parent].getKey() < bottom.getKey())
            {
                heapArray[index] = heapArray[parent];  // move it down
                index = parent;
                parent = (parent - 1) / 2;
            }  // end while
            heapArray[index] = bottom;
        }  // end trickleUp()

 

(2)移除操作

  1. 删除根节点,以最后一个节点来替换
  2. 取根点的2个子节点比较,取大值交换,直到层结束
            public void trickleDown(int index)
          {
          int largerChild;
          Node top = heapArray[index];       // save root
          while(index < currentSize/2)       // while node has at
             {                               //    least one child,
            
              int leftChild = 2*index+1;
             int rightChild = leftChild+1;
                                             // find larger child
             if(rightChild < currentSize &&  // (rightChild exists?)
                                 heapArray[leftChild].getKey() <
                                 heapArray[rightChild].getKey())
                largerChild = rightChild;
             else
                largerChild = leftChild;
                                             // top >= largerChild?
             if( top.getKey() >= heapArray[largerChild].getKey() )
                break;
                                             // shift child up
             heapArray[index] = heapArray[largerChild];
             index = largerChild;            // go down
              System.out.println( index+"/"+currentSize/2);
             }  // end while
          heapArray[index] = top;            // root to index
          }  // end trickleDown()
    

(3)更改操作

如果替换的值比当前值大的话,则向上找,否则向下找.

        public boolean change(int index, int newValue)
        {
            if (index < 0 || index >= currentSize)
                return false;
            int oldValue = heapArray[index].getKey(); // remember old
            heapArray[index].setKey(newValue);  // change to new

            if (oldValue < newValue)             // if raised,
                trickleUp(index);                // trickle it up
            else                                // if lowered,
                trickleDown(index);              // trickle it down
            return true;
        }  // end change()

十一.堆排序

当每次删除时,根总是最大的,所以一直取remove的值就可以获取堆排序

     for(j=size-1; j>=0; j--)    // remove from heap and
{                        //    store at array end
Node biggestNode = theHeap.remove();
System.out.print(biggestNode.getKey()+",");
theHeap.insertAt(j, biggestNode);
}

至此,更新完毕