整理的排序算法总结

来源:互联网 发布:闭口粉刺怎么去除知乎 编辑:程序博客网 时间:2024/05/18 13:28
一,三种代价为O(n*n)的排序算法
(1)插入排序
思路:逐个处理待排序的纪录,每个新纪录与前面的已经排序的子序列进行比较,插入正确的位置中。
(从小到大)
public static void insertSort(int[] A) {
          for (int i = 1; i < A.length; i ++)
              for (int j = i ; j > 0 && A[j] < A[j - 1]; j--) {
                  int temp = A[j];
                 A[j] = A[j - 1];
                 A[j - 1] = temp;
             }
    }
最佳时间 比较O(n)    序列按照从小到大排列好
最差时间 比较O(n*n)  系列完全反过来排列
(2)冒泡排序
    思路:每次从序列最后一个往上比较,把这趟最小的冒泡至上面

public static void bubbleSort(int[] A) {
          for (int i = 0; i < A.length; i++)
              for (int j = A.length - 1; j > i; j--) {
                  if (A[j] < A[j - 1]) {
                       int temp = A[j];
                      A[j] = A[j - 1];
                      A[j - 1] = temp;
                 }
             }
    }
时间:O(N*N)
(3)选择排序
思路:每次找到未排序中的最小的值交换一下即可,交换n-1次
public static void selectSort(int[] A) {
          for (int i = 0; i < A.length - 1; i++) {
              int lowIndex = i;
              for (int j = i+1; j <A.length; j++) {
                  if (A[j] < A[lowIndex])
                      lowIndex = j;
             }
              if(lowIndex!=i){
              int temp = A[lowIndex];
             A[lowIndex] = A[i];
             A[i] = temp;
             }
         }
    }
时间:O(N*N) 比较次数还是n*n,但是交换的次数只是n-1比冒泡排序少很多,选择排序的实质就是冒泡排序!!!
(2)代价为N(1.5)次方的Shell排序
思路:将序列分成子序列,然后分别对子序列进行排序,最后将子序列组合起来
(每个子序列用插入排序方法进行排序)
//incr 是增量 n是数组长度
void insertSortForSehll(int A[],int n,int incr){
     for(int i = incr;i<n;i+=incr)
     for(int j=i;(j>=incr)&&A[j]<A[j-incr];j-=incr)
         swap(A,j,j-incr);
}

void shellSort(int A[],int n){
   for(int i=n/2;i>2;i/=2    ){
     for(int j=0;j<i;j++)
        insertSort(&A[j],n-j,i);
   }
   insertSort(A,n,1);
}
3.代价为O(n*longN)的算法
(1)快速排序
int partition(int A[] ,int l,int r,int pivot){
    do{
       while(A[++l]<pivot)
       while(r!=0&&A[--r]>pivot);
       swap(A[l],A[r]);
       }while(l<r);
  swap(A[l],A[r]);  //do while 导致多了一次交换,会把l和r换一次,所以需要一次换回来(这个其实不太好,主要是条件不对)
return l;
}

void  qsort(int A[],int i,int j){
      if(i<=j)return ;
      int pivotIndex = findPivot(A,i,j);
      swap(A,pivotIndex,j);
      int k = partition(A,i-1,j,A[j]);
      swap(A,k,j);
      qsort(A,i,k-1);
      qsort(A,k+1,j);
}
时间:O(N*logN)
(2)归并排序
(感觉和shell排序是反过来 的)
36 20 17 13 28 14 23 15
20 36|13 1714|28|15 23
13 17 20 36|14 15 23 28
13 14 15 17 20 23 28 36
    privateint[] MergeSort(int[] i_list) {
        if(i_list.length == 1) {
            returni_list;
        }else{
            int[] listL = new int[i_list.length / 2];
            int[] listR = new int[i_list.length - i_list.length / 2];
            intCenter = i_list.length / 2;
            for(inti = 0; i < Center; i++) {
                listL[i] = i_list[i];
            }
            for(inti = Center, j = 0; i < i_list.length; i++, j++) {
                listR[j] = i_list[i];
            }
 
            int[] SortedListL=MergeSort(listL);
            int[] SortedListR=MergeSort(listR);
            int[] o_list = MergeTwoList(SortedListL, SortedListR);
            returno_list;
        }
    } 
 privateint[] MergeTwoList(int[] listL, int[] listR) {
        inti = 0, j = 0;
        int[] o_list = new int[listL.length + listR.length];
        intfoot = 0;
        while(i < listL.length && j < listR.length) {
            if(listL[i] <= listR[j]) {
                o_list[foot] = listL[i];
                i++;
            }else{
                o_list[foot] = listR[j];
                j++;
            }
            foot++;
        }
 
        if(i == listL.length) {
            while(j < listR.length) {
                o_list[foot++] = listR[j++];
            }
        }else{ // j==listR.length
            while(i < listL.length) {
                o_list[foot++] = listL[i++];
            }
        }
        returno_list;
    }
}
(3)堆排序
定义:堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。
思路:
1.建堆 o(n)
2.删除堆最大值放入数组的末尾
3.删除之后继续进行维持堆性质
4,直到堆为空

通常“堆”是通过数组来实现,这样可以利用数组的特点快速定位指定索引的元素。

当面试的时候被问到,你对堆有没有做过一些了解??

当时我我反问了一下:您是指堆数据结构还是JVM里面存储对象等信息的堆时??

当时感觉自己还挺高端的,居然可以反问,但是当面试官说是堆排序的时候我就傻眼了,

不会,只记得个大概,于是今天花了点时候把堆排序搞懂了!!

详解如下:

对于一个数组:

int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };

他可以用来表示堆,堆是一颗完全二叉树,分大值堆和小值堆,最后得出的分别是从大到小的排列顺序和从小到达的排列顺序,看你要排序的顺序觉定大小堆!

                         0(1)

         4 (2)                       1(3)

      3(4)          2(5)          16(6)       9(7)

10(8)   14(9)    8(10)     7(11)

存在一个如上图的关系,

left[i] = 2* i ;

right[i] = 2+i +1;

知道这个关系后,我们就可以开始建堆了:

从 a.length/2 到第一个元素 开始对每个元素进行建堆(递归),使每个元素下的树都维持最小值堆的特性;

a.length/2+1 到 a.length 个元素都是叶子节点,所以不需要维持最小值堆操作,因为已经是最小值堆了!!

代码如下:

public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


上一步骤理解了,那么之后的操作就很简单了。

建好最小值堆后,每次取堆顶元素和数组最后一个元素(最后第n个元素,n为交换次数)交换,剩下的元素采用维持堆性质的操作,

循环至最后堆只剩下一个元素即可,代码如下:

public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}

时间复杂度O(N*logN)

至此,堆排序讲解完毕!希望大家可以明白堆排序的原理了!

附上完整可运行代码:


package ddl.com;


public class HeapSort {
public static int heap_size;


// 双亲编号
public static int parent(int i) {
return i / 2;
}


// 左孩子编号
public static int leftChild(int i) {
return 2 * i;
}


// 右孩子编号
public static int rightChild(int i) {
return 2 * i + 1;
}


/**
* 堆排序:首先使用建立最大堆的算法建立好最大堆,然后将堆顶元素(最大值)与最后一个值交换,同时使得堆的长度减小1
* ,调用保持最大堆性质的算法调整,使得堆顶元素成为最大值,此时最后一个元素已被排除在外、
*/
public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}


/**
* 建立最大堆。在数据中,a.length/2+1一直到最后的元素都是叶子元素,也就是平凡最大堆,因此从其前一个元素开始,一直到
* 第一个元素,重复调用max_heapify函数,使其保持最大堆的性质

* @param a
*/
public static void build_max_heap(int[] a) {
for (int i = a.length / 2; i >= 1; i--) {
max_heapify(a, i);
}
}


/**
* 保持最大堆的性质

* @param a
*            ,堆中的数组元素
* @param i
*            ,对以该元素为根元素的堆进行调整,假设前提:左右子树都是最大堆

*            由于左右孩子都是最大堆,首先比较根元素与左右孩子,找出最大值,假如不是根元素,则调整两个元素的值;
*            由于左孩子(右孩子)的值与根元素交换,有可能打破左子树(右子树)的最大堆性质,因此继续调用,直至叶子元素。
*/
public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


public static void main(String[] args) {
int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
heap_size = a.length;
heapSort(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + "  ");
}
}
}
4分配排序和基数排序
(1)分配排序和位排序差不多O(n)典型的以空间换时间
for(i = 0;i<n;i++)
   B[A[i]] = A[[i];
基数排序:

第一步

以LSD为例,假设原来有一串数值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39

第二步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再进行一次分配,这次是根据十位数来分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93

第三步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。

2效率分析

时间效率:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针。

O(n*longN)

public class RadixSort {
      public static void sort( int[] number, int d) {
           int k = 0;
           int n = 1;
           int m = 1;// 控制键值排序依据在哪一位
           int [][] temp = new int [number.length ][number. length];
           int [] order = new int[number. length];
           while (m <= d) {
               for (int i = 0; i < number. length; i++) {
                    int lsd = ((number[i] / n) % 10);
                   temp[lsd][order[lsd]] = number[i];
                   order[lsd]++;
              }
               for (int i = 0; i < d; i++) {
                    if (order[i] != 0)
                         for (int j = 0; j < order[i]; j++) {
                             number[k] = temp[i][j];
                             k++;
                        }
                   order[i] = 0;
              }
              n *= 10;
              k = 0;
              m++;
          }
     }

      public static void main(String[] args) {
           int [] data = { 73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100 };
          RadixSort. sort(data, 10);
           for (int i = 0; i < data. length; i++) {
              System. out .print(data[i] + " " );
          }
     }
}


































0 0
原创粉丝点击