java中的各种排序算法

 

各种排序算法:

冒泡排序,选择排序,插入排序,稀尔排序,快速排序,归并排序,堆排序,桶式排序,基数排序

一、冒泡排序(Bubble Sort)

1. 基本思想:

  两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。

2. 排序过程:

  设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。

【示例】:

49 13 13 13 13 13 13 13

38 49 27 27 27 27 27 27

65 38 49 38 38 38 38 38

97 65 38 49 49 49 49 49

76 97 65 49 49 49 49 49

13 76 97 65 65 65 65 65

27 27 76 97 76 76 76 76

49 49 49 76 97 97 97 97

java代码实现:

Java代码 复制代码 收藏代码
  1. /**
  2. * 冒泡排序:
  3. * 执行完一次内for循环后,最小的一个数放到了数组的最前面,相邻位置之间交换
  4. * @author TSW
  5. *
  6. */ 
  7. public class BubbleSort { 
  8.  
  9.     public staticvoid main(String[] args) { 
  10.         Integer[] intgArr = { 7, 2, 4, 3, 12,1, 9,6, 8,5, 11,10 }; 
  11.         BubbleSort bubblesort = new BubbleSort(); 
  12.         bubblesort.bubble(intgArr, 0, intgArr.length -1); 
  13.         for (Integer intObj : intgArr) { 
  14.             System.out.print(intObj + " "); 
  15.         } 
  16.     } 
  17.      
  18.     /**
  19.      * 排序算法的实现,对数组中指定的元素进行排序  
  20.      * @param array 待排序的数组    
  21.      * @param from 从哪里开始排序  
  22.      * @param end 排到哪里
  23.      */ 
  24.     public void bubble(Integer[] array,int from, int end) { 
  25.  
  26.         // 需 array.length - 1 轮比较 
  27.  
  28.         for (int k =1; k < end - from + 1; k++) { 
  29.  
  30.             // 每轮循环中从最后一个元素开始向前起泡,直到i=k止,即i等于轮次止 
  31.  
  32.             for (int i = end - from; i >= k; i--) { 
  33.  
  34.                 // 按照一种规则(后面元素不能小于前面元素)排序 
  35.  
  36.                 if ((array[i].compareTo(array[i -1])) < 0) { 
  37.  
  38.                     // 如果后面元素小于了(当然是大于还是小于要看比较器实现了)前面的元素,则前后交换 
  39.  
  40.                     swap(array, i, i - 1); 
  41.  
  42.                 } 
  43.  
  44.             } 
  45.  
  46.         } 
  47.  
  48.     } 
  49.      
  50.     /**
  51.      * 交换数组中的两个元素的位置  
  52.      * @param array 待交换的数组  
  53.      * @param i 第一个元素  
  54.      * @param j 第二个元素  
  55.      */ 
  56.     public void swap(Integer[] array,int i, int j) { 
  57.  
  58.         if (i != j) {// 只有不是同一位置时才需交换 
  59.  
  60.             Integer tmp = array[i]; 
  61.  
  62.             array[i] = array[j]; 
  63.  
  64.             array[j] = tmp; 
  65.  
  66.         } 
  67.  
  68.     } 
  69.  
/**
 * 冒泡排序:
 * 执行完一次内for循环后,最小的一个数放到了数组的最前面,相邻位置之间交换 
 * @author TSW
 *
 */
public class BubbleSort {

	public static void main(String[] args) {
		Integer[] intgArr = { 7, 2, 4, 3, 12, 1, 9, 6, 8, 5, 11, 10 };
		BubbleSort bubblesort = new BubbleSort();
		bubblesort.bubble(intgArr, 0, intgArr.length - 1);
		for (Integer intObj : intgArr) {
			System.out.print(intObj + " ");
		}
	}
	
	/**
	 * 排序算法的实现,对数组中指定的元素进行排序   
	 * @param array 待排序的数组     
	 * @param from 从哪里开始排序   
	 * @param end 排到哪里
	 */
	public void bubble(Integer[] array, int from, int end) {

		// 需 array.length - 1 轮比较

		for (int k = 1; k < end - from + 1; k++) {

			// 每轮循环中从最后一个元素开始向前起泡,直到i=k止,即i等于轮次止

			for (int i = end - from; i >= k; i--) {

				// 按照一种规则(后面元素不能小于前面元素)排序

				if ((array[i].compareTo(array[i - 1])) < 0) {

					// 如果后面元素小于了(当然是大于还是小于要看比较器实现了)前面的元素,则前后交换

					swap(array, i, i - 1);

				}

			}

		}

	}
	
	/**
	 * 交换数组中的两个元素的位置   
	 * @param array 待交换的数组   
	 * @param i 第一个元素   
	 * @param j 第二个元素   
	 */
	public void swap(Integer[] array, int i, int j) {

		if (i != j) {// 只有不是同一位置时才需交换

			Integer tmp = array[i];

			array[i] = array[j];

			array[j] = tmp;

		}

	}

}

另外一种实现方式:

Java代码 复制代码 收藏代码
  1. /**
  2. * 冒泡排序:执行完一次内for循环后,最大的一个数放到了数组的最后面。相邻位置之间交换
  3. */ 
  4.  
  5. public class BubbleSort2 { 
  6.  
  7.     public staticvoid main(String[] args) { 
  8.  
  9.         int[] a = { 3,5, 9,4, 7,8, 6,1, 2 }; 
  10.  
  11.         BubbleSort2 bubble = new BubbleSort2(); 
  12.  
  13.         bubble.bubble(a); 
  14.  
  15.         for (int num : a) { 
  16.  
  17.             System.out.print(num + " "); 
  18.  
  19.         } 
  20.  
  21.     } 
  22.  
  23.     public void bubble(int[] a) { 
  24.  
  25.         for (int i = a.length -1; i > 0; i--) { 
  26.  
  27.             for (int j =0; j < i; j++) { 
  28.  
  29.                 if (new Integer(a[j]).compareTo(new Integer(a[j +1])) > 0) { 
  30.  
  31.                     swap(a, j, j + 1); 
  32.  
  33.                 } 
  34.  
  35.             } 
  36.  
  37.         } 
  38.  
  39.     } 
  40.  
  41.     public void swap(int[] a,int x, int y) { 
  42.  
  43.         int temp; 
  44.  
  45.         temp = a[x]; 
  46.  
  47.         a[x] = a[y]; 
  48.  
  49.         a[y] = temp; 
  50.  
  51.     } 
  52.  
/**
 * 冒泡排序:执行完一次内for循环后,最大的一个数放到了数组的最后面。相邻位置之间交换
 */

public class BubbleSort2 {

	public static void main(String[] args) {

		int[] a = { 3, 5, 9, 4, 7, 8, 6, 1, 2 };

		BubbleSort2 bubble = new BubbleSort2();

		bubble.bubble(a);

		for (int num : a) {

			System.out.print(num + " ");

		}

	}

	public void bubble(int[] a) {

		for (int i = a.length - 1; i > 0; i--) {

			for (int j = 0; j < i; j++) {

				if (new Integer(a[j]).compareTo(new Integer(a[j + 1])) > 0) {

					swap(a, j, j + 1);

				}

			}

		}

	}

	public void swap(int[] a, int x, int y) {

		int temp;

		temp = a[x];

		a[x] = a[y];

		a[y] = temp;

	}

}

 

 

二、选择排序

1. 基本思想:

  每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。

2. 排序过程:

【示例】:

  初始关键字[49 38 65 97 76 13 27 49]

第一趟排序后13 [38 65 97 76 49 27 49]

第二趟排序后13 27 [65 97 76 49 38 49]

第三趟排序后13 27 38 [97 76 49 65 49]

第四趟排序后13 27 38 49 [49 97 65 76]

第五趟排序后13 27 38 49 49 [97 97 76]

第六趟排序后13 27 38 49 49 76 [76 97]

第七趟排序后13 27 38 49 49 76 76 [ 97]

最后排序结果13 27 38 49 49 76 76 97

java代码实现:

Java代码 复制代码 收藏代码
  1. package test.suanfa; 
  2.  
  3. public class SelectSort { 
  4.  
  5.     public staticvoid main(String[] args) { 
  6.         Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 }; 
  7.         SelectSort insertSort = new SelectSort(); 
  8.         insertSort.select(intgArr); 
  9.         for (Integer intObj : intgArr) { 
  10.             System.out.print(intObj + " "); 
  11.         } 
  12.     } 
  13.  
  14.     public void select(Integer[] array) { 
  15.  
  16.         int minIndex;// 最小索引 
  17.  
  18.         /*
  19.          *
  20.          * 循环整个数组(其实这里的上界为array.length - 1 即可,因为当i= array.length-1
  21.          *
  22.          * 时,最后一个元素就已是最大的了,如果为array.length时,内层循环将不再循环),每轮假设
  23.          *
  24.          * 第一个元素为最小元素,如果从第一元素后能选出比第一个元素更小元素,则让让最小元素与第一
  25.          *
  26.          * 个元素交换
  27.          */ 
  28.  
  29.         for (int i =0; i < array.length; i++) { 
  30.  
  31.             minIndex = i;// 假设每轮第一个元素为最小元素 
  32.  
  33.             // 从假设的最小元素的下一元素开始循环 
  34.  
  35.             for (int j = i +1; j < array.length; j++) { 
  36.  
  37.                 // 如果发现有比当前array[smallIndex]更小元素,则记下该元素的索引于smallIndex中 
  38.  
  39.                 if ((array[j].compareTo(array[minIndex])) <0) { 
  40.  
  41.                     minIndex = j; 
  42.  
  43.                 } 
  44.  
  45.             } 
  46.  
  47.             // 先前只是记录最小元素索引,当最小元素索引确定后,再与每轮的第一个元素交换 
  48.  
  49.             swap(array, i, minIndex); 
  50.  
  51.         } 
  52.  
  53.     } 
  54.  
  55.     public staticvoid swap(Integer[] intgArr, int x,int y) { 
  56.  
  57.         // Integer temp; //这个也行 
  58.  
  59.         int temp; 
  60.  
  61.         temp = intgArr[x]; 
  62.  
  63.         intgArr[x] = intgArr[y]; 
  64.  
  65.         intgArr[y] = temp; 
  66.  
  67.     } 
  68.  

 

 
 

、插入排序(Insertion Sort)

1. 基本思想:

  每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。

2. 排序过程: 

【示例】:

[初始关键字] [49] 38 65 97 76 13 27 49

    J=2(38) [38 49] 65 97 76 13 27 49

    J=3(65) [38 49 65] 97 76 13 27 49

    J=4(97) [38 49 65 97] 76 13 27 49

    J=5(76) [38 49 65 76 97] 13 27 49

    J=6(13) [13 38 49 65 76 97] 27 49

    J=7(27) [13 27 38 49 65 76 97] 49

    J=8(49) [13 27 38 49 49 65 76 97]

java代码实现:

Java代码 复制代码 收藏代码
  1. package test.suanfa;   
  2.    
  3. public class InsertSort {   
  4.        
  5.     /**
  6.      * 测试
  7.      */   
  8.     public staticvoid main(String[] args) {   
  9.         Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };   
  10.         InsertSort insertSort = new InsertSort();   
  11.         insertSort.insert(intgArr, 0, intgArr.length -1);   
  12.         for (Integer intObj : intgArr) {   
  13.             System.out.print(intObj + " ");   
  14.         }   
  15.     }   
  16.    
  17.     /**
  18.      * 排序算法的实现,对数组中指定的元素进行排序
  19.      * @param array 待排序的数组
  20.      * @param from 从哪里开始排序
  21.      * @param end 排到哪里
  22.      */   
  23.     public void insert(Integer[] array,int from, int end) {   
  24.            
  25.         /*
  26.          * 第一层循环:对待插入(排序)的元素进行循环
  27.          * 
  28.          * 从待排序数组的第二个元素开始循环,到最后一个元素(包括)止
  29.          */   
  30.            
  31.         for (int i = from +1; i <= end; i++) {   
  32.    
  33.             /*
  34.              * 
  35.              * 第二层循环:对有序数组进行循环,且从有序数组最第一个元素开始向后循环
  36.              * 
  37.              * 找到第一个大于待插入的元素
  38.              * 
  39.              * 有序数组初始元素只有一个,且为原数组的第一个元素,一个元素数组总是有序的
  40.              */   
  41.    
  42.             for (int j =0; j < i; j++) {   
  43.    
  44.                 Integer insertedElem = array[i];// 待插入到有序数组的元素  
  45.    
  46.                 // 从有序数组中第一个元素开始查找第一个大于待插入的元素   
  47.    
  48.                 if ((array[j].compareTo(insertedElem)) >0) {   
  49.    
  50.                     // 找到插入点后,从插入点开始向后所有元素后移一位   
  51.    
  52.                     move(array, j, i - 1);   
  53.    
  54.                     // 将待排序元素插入到有序数组中   
  55.    
  56.                     array[j] = insertedElem;   
  57.    
  58.                     break;   
  59.                 }   
  60.    
  61.             }   
  62.    
  63.         }   
  64.    
  65.         // =======以下是java.util.Arrays的插入排序算法的实现   
  66.    
  67.         /*
  68.          * 
  69.          * 该算法看起来比较简洁一j点,有点像冒泡算法。
  70.          * 
  71.          * 将数组逻辑上分成前后两个集合,前面的集合是已经排序好序的元素,而后面集合为待排序的
  72.          * 
  73.          * 集合,每次内层循从后面集合中拿出一个元素,通过冒泡的形式,从前面集合最后一个元素开
  74.          * 
  75.          * 始往前比较,如果发现前面元素大于后面元素,则交换,否则循环退出
  76.          * 
  77.          * 
  78.          * 总感觉这种算术有点怪怪,既然是插入排序,应该是先找到插入点,而后再将待排序的元素插
  79.          * 
  80.          * 入到的插入点上,那么其他元素就必然向后移,感觉算法与排序名称不匹,但返过来与上面实
  81.          * 
  82.          * 现比,其实是一样的,只是上面先找插入点,待找到后一次性将大的元素向后移,而该算法却
  83.          * 
  84.          * 是走一步看一步,一步一步将待排序元素往前移
  85.          */   
  86.    
  87.         /*
  88.          * 
  89.          * for (int i = from; i <= end; i++) {
  90.          * 
  91.          * for (int j = i; j > from && c.compare(array[j - 1], array[j]) > 0;
  92.          * j--) {
  93.          * 
  94.          * swap(array, j, j - 1);
  95.          * 
  96.          * }
  97.          * 
  98.          * }
  99.          */   
  100.    
  101.     }   
  102.    
  103.     /**
  104.      * 数组元素后移
  105.      * @param array 待移动的数组
  106.      * @param startIndex 从哪个开始移
  107.      * @param endIndex 到哪个元素止
  108.      */   
  109.     public void move(Integer[] array,int startIndex, int endIndex) {   
  110.         for (int i = endIndex; i >= startIndex; i--) {   
  111.             array[i + 1] = array[i];   
  112.         }   
  113.     }   
  114.    
  115. }   
    package test.suanfa;  
      
    public class InsertSort {  
          
        /** 
         * 测试 
         */  
        public static void main(String[] args) {  
            Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };  
            InsertSort insertSort = new InsertSort();  
            insertSort.insert(intgArr, 0, intgArr.length - 1);  
            for (Integer intObj : intgArr) {  
                System.out.print(intObj + " ");  
            }  
        }  
      
        /** 
         * 排序算法的实现,对数组中指定的元素进行排序 
         * @param array 待排序的数组 
         * @param from 从哪里开始排序 
         * @param end 排到哪里 
         */  
        public void insert(Integer[] array, int from, int end) {  
              
            /* 
             * 第一层循环:对待插入(排序)的元素进行循环 
             *  
             * 从待排序数组的第二个元素开始循环,到最后一个元素(包括)止 
             */  
              
            for (int i = from + 1; i <= end; i++) {  
      
                /* 
                 *  
                 * 第二层循环:对有序数组进行循环,且从有序数组最第一个元素开始向后循环 
                 *  
                 * 找到第一个大于待插入的元素 
                 *  
                 * 有序数组初始元素只有一个,且为原数组的第一个元素,一个元素数组总是有序的 
                 */  
      
                for (int j = 0; j < i; j++) {  
      
                    Integer insertedElem = array[i];// 待插入到有序数组的元素  
      
                    // 从有序数组中第一个元素开始查找第一个大于待插入的元素  
      
                    if ((array[j].compareTo(insertedElem)) > 0) {  
      
                        // 找到插入点后,从插入点开始向后所有元素后移一位  
      
                        move(array, j, i - 1);  
      
                        // 将待排序元素插入到有序数组中  
      
                        array[j] = insertedElem;  
      
                        break;  
                    }  
      
                }  
      
            }  
      
            // =======以下是java.util.Arrays的插入排序算法的实现  
      
            /* 
             *  
             * 该算法看起来比较简洁一j点,有点像冒泡算法。 
             *  
             * 将数组逻辑上分成前后两个集合,前面的集合是已经排序好序的元素,而后面集合为待排序的 
             *  
             * 集合,每次内层循从后面集合中拿出一个元素,通过冒泡的形式,从前面集合最后一个元素开 
             *  
             * 始往前比较,如果发现前面元素大于后面元素,则交换,否则循环退出 
             *  
             *  
             * 总感觉这种算术有点怪怪,既然是插入排序,应该是先找到插入点,而后再将待排序的元素插 
             *  
             * 入到的插入点上,那么其他元素就必然向后移,感觉算法与排序名称不匹,但返过来与上面实 
             *  
             * 现比,其实是一样的,只是上面先找插入点,待找到后一次性将大的元素向后移,而该算法却 
             *  
             * 是走一步看一步,一步一步将待排序元素往前移 
             */  
      
            /* 
             *  
             * for (int i = from; i <= end; i++) { 
             *  
             * for (int j = i; j > from && c.compare(array[j - 1], array[j]) > 0; 
             * j--) { 
             *  
             * swap(array, j, j - 1); 
             *  
             * } 
             *  
             * } 
             */  
      
        }  
      
        /** 
         * 数组元素后移 
         * @param array 待移动的数组 
         * @param startIndex 从哪个开始移 
         * @param endIndex 到哪个元素止 
         */  
        public void move(Integer[] array, int startIndex, int endIndex) {  
            for (int i = endIndex; i >= startIndex; i--) {  
                array[i + 1] = array[i];  
            }  
        }  
      
    }  
 
 
 

四、希尔排序(Shell Sort)

插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

希尔排序基本思想:

  先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。   

      该方法实质上是一种分组插入方法。

  给定实例的shell排序的排序过程

  假设待排序文件有10个记录,其关键字分别是:

  49,38,65,97,76,13,27,49,55,04。

  增量序列的取值依次为:

  5,3,1

缩小增量法

  属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序

    排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止

    初始:d=5

  49 38 65 97 76 13 27 49* 55 04

  49 13

  |-------------------|

  38 27

  |-------------------|

  65 49*

  |-------------------|

  97 55

  |-------------------|

  76 04

  |-------------------|

  一趟结果

    13 27 49* 55 04 49 38 65 97 76

  d=3

  13 27 49* 55 04 49 38 65 97 76

  13 55 38 76

  |------------|------------|------------|

  27 04 65

  |------------|------------|

  49* 49 97

  |------------|------------|

      二趟结果

  13 04 49* 38 27 49 55 65 97 76

  d=1

  13 04 49* 38 27 49 55 65 97 76

  |----|----|----|----|----|----|----|----|----|

  三趟结果

    04 13 27 38 49* 49 55 65 76 97

算法分析:

不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法,  在此算法基础之上增加了一个新的特性,提高了效率。希尔排序的时间复杂度为 O(N*(logN)2), 没有快速排序算法快 O(N*(logN)),因此中等大小规模表现良好,对规模非常大的数据排序不是  最优选择。但是比O(N2)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。  此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏 的情况下执行的效率会非常差。  专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快, 再改成快速排序这样更高级的排序算法. 本质上讲,希尔排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当N值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。  当N值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。

稳定性:

稳定性

排序前一个序列中,如果出现N个与关键字相同的数据,那么排序后仍然按照原先序列的排列顺序排列,就说这个算法是稳定的,反之就是不稳定的。通俗地讲就是 能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj,  Ai原来在位置前,排序后Ai还是要在Aj位置前。

希尔分析

希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小, 插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元 素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

Java 代码实现:

Java代码 复制代码 收藏代码
  1. package test.suanfa; 
  2.  
  3. /**
  4. * 插入排序----希尔排序:我们选择步长为:15,7,3,1 我们选择步长公式为:2^k-1,2^(k-1)-1,……,15,7,3,1
  5. * (2^4-1,2^3-1,2^2-1,2^1-1) 注意所有排序都是从小到大排。
  6. *
  7. * @author TSW
  8. */ 
  9. public class ShellSort { 
  10.      
  11.     /**
  12.      * 测试  
  13.      * @param args
  14.      */ 
  15.     public staticvoid main(String[] args) {       
  16.         Integer[] intgArr = { 5, 9, 1, 4, 8, 2, 6, 3, 7, 0 }; 
  17.         ShellSort shellSort = new ShellSort(); 
  18.         shellSort.sort(intgArr, 0, intgArr.length -1); 
  19.         for (Integer intObj : intgArr) { 
  20.             System.out.print(intObj + " "); 
  21.         } 
  22.     }       
  23.  
  24.     /**
  25.      * 排序算法的实现,对数组中指定的元素进行排序 
  26.      * @param array 待排序的数组  
  27.      * @param from 从哪里开始排序  
  28.      * @param end 排到哪里  
  29.      */ 
  30.     public void sort(Integer[] array,int from, int end) { 
  31.  
  32.         // 初始步长,实质为每轮的分组数 
  33.  
  34.         int step = initialStep(end - from +1); 
  35.  
  36.         // 第一层循环是对排序轮次进行循环。(step + 1) / 2 - 1 为下一轮步长值 
  37.  
  38.         for (; step >= 1; step = (step +1) / 2 -1) { 
  39.  
  40.             // 对每轮里的每个分组进行循环 
  41.  
  42.             for (int groupIndex =0; groupIndex < step; groupIndex++) { 
  43.  
  44.                 // 对每组进行直接插入排序 
  45.  
  46.                 insertSort(array, groupIndex, step, end); 
  47.  
  48.             } 
  49.  
  50.         } 
  51.  
  52.     } 
  53.  
  54.     /**
  55.      * 直接插入排序实现
  56.      * @param array 待排序数组  
  57.      * @param groupIndex 对每轮的哪一组进行排序 
  58.      * @param step 步长  
  59.      * @param end 整个数组要排哪个元素止  
  60.      */ 
  61.     public void insertSort(Integer[] array,int groupIndex, int step,int end) { 
  62.  
  63.         int startIndex = groupIndex;// 从哪里开始排序 
  64.  
  65.         int endIndex = startIndex;// 排到哪里 
  66.  
  67.         /*
  68.          *
  69.          * 排到哪里需要计算得到,从开始排序元素开始,以step步长,可求得下元素是否在数组范围内,
  70.          *
  71.          * 如果在数组范围内,则继续循环,直到索引超现数组范围
  72.          */ 
  73.  
  74.         while ((endIndex + step) <= end) { 
  75.  
  76.             endIndex += step; 
  77.  
  78.         } 
  79.  
  80.         // i为每小组里的第二个元素开始 
  81.  
  82.         for (int i = groupIndex + step; i <= end; i += step) { 
  83.  
  84.             for (int j = groupIndex; j < i; j += step) { 
  85.  
  86.                 Integer insertedElem = array[i]; 
  87.  
  88.                 // 从有序数组中最一个元素开始查找第一个大于待插入的元素 
  89.  
  90.                 if ((array[j].compareTo(insertedElem)) >=0) { 
  91.  
  92.                     // 找到插入点后,从插入点开始向后所有元素后移一位 
  93.  
  94.                     move(array, j, i - step, step); 
  95.  
  96.                     array[j] = insertedElem; 
  97.  
  98.                     break
  99.  
  100.                 } 
  101.  
  102.             } 
  103.  
  104.         } 
  105.  
  106.     } 
  107.  
  108.     /**
  109.      * 根据数组长度求初始步长  
  110.      *
  111.      * 我们选择步长的公式为:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 减一即为该步长序列,k 为排序轮次   
  112.      *
  113.      * 初始步长:step = 2^k-1   
  114.      * 初始步长约束条件:step < len - 1 初始步长的值要小于数组长度还要减一的值
  115.      * (因 为第一轮分组时尽量不要分为一组,除非数组本身的长度就小于等于4)
  116.      *
  117.      * 由上面两个关系试可以得知:2^k - 1 < len - 1 关系式,其中k为轮次,如果把2^k 表 达式  
  118.      * 转换成step 表达式,则2^k-1 可使用(step + 1)*2-1 替换(因为step+1 相当于第k-1 
  119.      * 轮的步长,所以在step+1 基础上乘以2 就相当于2^k 了),即步长与数组长度的关系不等式为
  120.      * (step + 1)*2 - 1 < len -1  
  121.      *
  122.      * @param len 数组长度  
  123.      * @return
  124.      */ 
  125.     public staticint initialStep(int len) { 
  126.  
  127.         /*
  128.          *
  129.          * 初始值设置为步长公式中的最小步长,从最小步长推导出最长初始步长值,即按照以下公式来推:
  130.          *
  131.          * 1,3,7,15,...,2^(k-1)-1,2^k-1
  132.          *
  133.          * 如果数组长度小于等于4时,步长为1,即长度小于等于4的数组不用分组,此时直接退化为直接插入排序
  134.          */ 
  135.  
  136.         int step = 1
  137.  
  138.         // 试探下一个步长是否满足条件,如果满足条件,则步长置为下一步长 
  139.  
  140.         while ((step + 1) *2 - 1 < len -1) { 
  141.  
  142.             step = (step + 1) * 2 - 1
  143.  
  144.         } 
  145.  
  146.         System.out.println("初始步长: " + step); 
  147.  
  148.         return step; 
  149.  
  150.     } 
  151.  
  152.     /**
  153.      * 以指定的步长将数组元素后移,步长指定每个元素间的间隔
  154.      * @param array 待排序数组  
  155.      * @param startIndex 从哪里开始移  
  156.      * @param endIndex 到哪个元素止  
  157.      * @param step 步长  
  158.      */ 
  159.     protected finalvoid move(Integer[] array, int startIndex,int endIndex, int step) { 
  160.         for (int i = endIndex; i >= startIndex; i -= step) { 
  161.             array[i + step] = array[i]; 
  162.         } 
  163.     } 
  164.  
 
 
 
 
 

五、快速排序(Quick Sort)

1. 基本思想:

  在当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置 上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划 分过程,直至所有无序子区中的数据元素均已排序为止。

2. 排序过程:

【示例】:

初始关键字 [49 38 65 97 76 13 27 49]

一趟排序之后 [27 38 13]49 [76 97 65 49]

二趟排序之后 [13]27 [38]49 [49 65]76 [97]

三趟排序之后13 27 38 49 49 [65]76 97

最后的排序结果13 27 38 49 49 65 76 97

java代码实现:

Java代码 复制代码 收藏代码
  1. package test.suanfa; 
  2.  
  3. public class QuickSort { 
  4.      
  5.     public staticvoid main(String[] args) { 
  6.         Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 }; 
  7.         QuickSort quicksort = new QuickSort(); 
  8.         quicksort.sort(intgArr, 0, intgArr.length -1); 
  9.         for (Integer intObj : intgArr) { 
  10.             System.out.print(intObj + " "); 
  11.         } 
  12.     } 
  13.  
  14.     public void sort(Integer[] array,int from, int end) { 
  15.         quickSort(array, from, end); 
  16.     } 
  17.  
  18.     private void quickSort(Integer[] array,int low, int high) { 
  19.  
  20.         /*
  21.          *
  22.          * 如果分区中的低指针小于高指针时循环;如果low=higth说明数组只有一个元素,无需再处理;
  23.          *
  24.          * 如果low > higth,则说明上次枢纽元素的位置pivot就是low或者是higth,此种情况
  25.          *
  26.          * 下分区不存,也不需处理
  27.          */ 
  28.  
  29.         if (low < high) { 
  30.  
  31.             // 对分区进行排序整理 
  32.  
  33.             // int pivot = partition1(array, low, high); 
  34.  
  35.             int pivot = partition2(array, low, high); 
  36.  
  37.             // int pivot = partition3(array, low, high); 
  38.  
  39.             /*
  40.              *
  41.              * 以pivot为边界,把数组分成三部分[low, pivot - 1]、[pivot]、[pivot + 1, high]
  42.              *
  43.              * 其中[pivot]为枢纽元素,不需处理,再对[low, pivot - 1]与[pivot + 1, high]
  44.              *
  45.              * 各自进行分区排序整理与进一步分区
  46.              */ 
  47.  
  48.             quickSort(array, low, pivot - 1); 
  49.  
  50.             quickSort(array, pivot + 1, high); 
  51.  
  52.         } 
  53.  
  54.     } 
  55.  
  56.     private int partition1(Integer[] array,int low, int high) { 
  57.  
  58.         Integer pivotElem = array[low];// 以第一个元素为中枢元素 
  59.  
  60.         // 从前向后依次指向比中枢元素小的元素,刚开始时指向中枢,也是小于与大小中枢的元素的分界点 
  61.  
  62.         int border = low; 
  63.  
  64.         /*
  65.          *
  66.          * 在中枢元素后面的元素中查找小于中枢元素的所有元素,并依次从第二个位置从前往后存放
  67.          *
  68.          * 注,这里最好使用i来移动,如果直接移动low的话,最后不知道数组的边界了,但后面需要
  69.          *
  70.          * 知道数组的边界
  71.          */ 
  72.  
  73.         for (int i = low +1; i <= high; i++) { 
  74.  
  75.             // 如果找到一个比中枢元素小的元素 
  76.  
  77.             if ((array[i].compareTo(pivotElem)) <0) { 
  78.  
  79.                 swap(array, ++border, i);// border前移,表示有小于中枢元素的元素 
  80.  
  81.             } 
  82.  
  83.         } 
  84.  
  85.         /*
  86.          *
  87.          * 如果border没有移动时说明说明后面的元素都比中枢元素要大,border与low相等,此时是
  88.          *
  89.          * 同一位置交换,是否交换都没关系;当border移到了high时说明所有元素都小于中枢元素,此
  90.          *
  91.          * 时将中枢元素与最后一个元素交换即可,即low与high进行交换,大的中枢元素移到了 序列最
  92.          *
  93.          * 后;如果low <minIndex< high,表 明中枢后面的元素前部分小于中枢元素,而后部分大于
  94.          *
  95.          * 中枢元素,此时中枢元素与前部分数组中最后一个小于它的元素交换位置,使得中枢元素放置在
  96.          *
  97.          * 正确的位置
  98.          */ 
  99.  
  100.         swap(array, border, low); 
  101.  
  102.         return border; 
  103.  
  104.     } 
  105.  
  106.     private int partition2(Integer[] array,int low, int high) { 
  107.  
  108.         int pivot = low;// 中枢元素位置,我们以第一个元素为中枢元素 
  109.  
  110.         // 退出条件这里只可能是low = high 
  111.  
  112.         while (true) { 
  113.  
  114.             if (pivot != high) {// 如果中枢元素在低指针位置时,我们移动高指针 
  115.  
  116.                 // 如果高指针元素小于中枢元素时,则与中枢元素交换 
  117.  
  118.                 if ((array[high].compareTo(array[pivot])) <0) { 
  119.  
  120.                     swap(array, high, pivot); 
  121.  
  122.                     // 交换后中枢元素在高指针位置了 
  123.  
  124.                     pivot = high; 
  125.  
  126.                 } else {// 如果未找到小于中枢元素,则高指针前移继续找 
  127.  
  128.                     high--; 
  129.  
  130.                 } 
  131.  
  132.             } else {// 否则中枢元素在高指针位置 
  133.  
  134.                 // 如果低指针元素大于中枢元素时,则与中枢元素交换 
  135.  
  136.                 if ((array[low].compareTo(array[pivot])) >0) { 
  137.  
  138.                     swap(array, low, pivot); 
  139.  
  140.                     // 交换后中枢元素在低指针位置了 
  141.  
  142.                     pivot = low; 
  143.  
  144.                 } else {// 如果未找到大于中枢元素,则低指针后移继续找 
  145.  
  146.                     low++; 
  147.  
  148.                 } 
  149.  
  150.             } 
  151.  
  152.             if (low == high) { 
  153.  
  154.                 break
  155.  
  156.             } 
  157.  
  158.         } 
  159.  
  160.         // 返回中枢元素所在位置,以便下次分区 
  161.  
  162.         return pivot; 
  163.  
  164.     } 
  165.  
  166.     private int partition3(Integer[] array,int low, int high) { 
  167.  
  168.         int pivot = low;// 中枢元素位置,我们以第一个元素为中枢元素 
  169.  
  170.         low++; 
  171.  
  172.         // ----调整高低指针所指向的元素顺序,把小于中枢元素的移到前部分,大于中枢元素的移到后面部分 
  173.  
  174.         // 退出条件这里只可能是low = high 
  175.  
  176.         while (true) { 
  177.  
  178.             // 如果高指针未超出低指针 
  179.  
  180.             while (low < high) { 
  181.                 // 如果低指针指向的元素大于或等于中枢元素时表示找到了,退出,注:等于时也要后移 
  182.                 if ((array[low].compareTo(array[pivot])) >=0) { 
  183.                     break
  184.                 } else {// 如果低指针指向的元素小于中枢元素时继续找 
  185.                     low++; 
  186.                 } 
  187.             } 
  188.  
  189.             while (high > low) { 
  190.  
  191.                 // 如果高指针指向的元素小于中枢元素时表示找到,退出 
  192.  
  193.                 if ((array[high].compareTo(array[pivot])) <0) { 
  194.  
  195.                     break
  196.  
  197.                 } else {// 如果高指针指向的元素大于中枢元素时继续找 
  198.  
  199.                     high--; 
  200.  
  201.                 } 
  202.  
  203.             } 
  204.  
  205.             // 退出上面循环时low = high 
  206.  
  207.             if (low == high) { 
  208.  
  209.                 break
  210.  
  211.             } 
  212.  
  213.             swap(array, low, high); 
  214.  
  215.         } 
  216.  
  217.         // ----高低指针所指向的元素排序完成后,还得要把中枢元素放到适当的位置 
  218.  
  219.         if ((array[pivot].compareTo(array[low])) >0) { 
  220.  
  221.             // 如果退出循环时中枢元素大于了低指针或高指针元素时,中枢元素需与low元素交换 
  222.  
  223.             swap(array, low, pivot); 
  224.  
  225.             pivot = low; 
  226.  
  227.         } else if ((array[pivot].compareTo(array[low])) <=0) { 
  228.  
  229.             swap(array, low - 1, pivot); 
  230.  
  231.             pivot = low - 1
  232.  
  233.         } 
  234.  
  235.         // 返回中枢元素所在位置,以便下次分区 
  236.  
  237.         return pivot; 
  238.  
  239.     } 
  240.  
  241.     public void swap(Integer[] array,int i, int j) { 
  242.  
  243.         if (i != j) {// 只有不是同一位置时才需交换 
  244.  
  245.             Integer tmp = array[i]; 
  246.  
  247.             array[i] = array[j]; 
  248.  
  249.             array[j] = tmp; 
  250.  
  251.         } 
  252.  
  253.     } 
  254.  
package test.suanfa;

public class QuickSort {
	
	public static void main(String[] args) {
		Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };
		QuickSort quicksort = new QuickSort();
		quicksort.sort(intgArr, 0, intgArr.length - 1);
		for (Integer intObj : intgArr) {
			System.out.print(intObj + " ");
		}
	}

	public void sort(Integer[] array, int from, int end) {
		quickSort(array, from, end);
	}

	private void quickSort(Integer[] array, int low, int high) {

		/*
		 * 
		 * 如果分区中的低指针小于高指针时循环;如果low=higth说明数组只有一个元素,无需再处理;
		 * 
		 * 如果low > higth,则说明上次枢纽元素的位置pivot就是low或者是higth,此种情况
		 * 
		 * 下分区不存,也不需处理
		 */

		if (low < high) {

			// 对分区进行排序整理

			// int pivot = partition1(array, low, high);

			int pivot = partition2(array, low, high);

			// int pivot = partition3(array, low, high);

			/*
			 * 
			 * 以pivot为边界,把数组分成三部分[low, pivot - 1]、[pivot]、[pivot + 1, high]
			 * 
			 * 其中[pivot]为枢纽元素,不需处理,再对[low, pivot - 1]与[pivot + 1, high]
			 * 
			 * 各自进行分区排序整理与进一步分区
			 */

			quickSort(array, low, pivot - 1);

			quickSort(array, pivot + 1, high);

		}

	}

	private int partition1(Integer[] array, int low, int high) {

		Integer pivotElem = array[low];// 以第一个元素为中枢元素

		// 从前向后依次指向比中枢元素小的元素,刚开始时指向中枢,也是小于与大小中枢的元素的分界点

		int border = low;

		/*
		 * 
		 * 在中枢元素后面的元素中查找小于中枢元素的所有元素,并依次从第二个位置从前往后存放
		 * 
		 * 注,这里最好使用i来移动,如果直接移动low的话,最后不知道数组的边界了,但后面需要
		 * 
		 * 知道数组的边界
		 */

		for (int i = low + 1; i <= high; i++) {

			// 如果找到一个比中枢元素小的元素

			if ((array[i].compareTo(pivotElem)) < 0) {

				swap(array, ++border, i);// border前移,表示有小于中枢元素的元素

			}

		}

		/*
		 * 
		 * 如果border没有移动时说明说明后面的元素都比中枢元素要大,border与low相等,此时是
		 * 
		 * 同一位置交换,是否交换都没关系;当border移到了high时说明所有元素都小于中枢元素,此
		 * 
		 * 时将中枢元素与最后一个元素交换即可,即low与high进行交换,大的中枢元素移到了 序列最
		 * 
		 * 后;如果low <minIndex< high,表 明中枢后面的元素前部分小于中枢元素,而后部分大于
		 * 
		 * 中枢元素,此时中枢元素与前部分数组中最后一个小于它的元素交换位置,使得中枢元素放置在
		 * 
		 * 正确的位置
		 */

		swap(array, border, low);

		return border;

	}

	private int partition2(Integer[] array, int low, int high) {

		int pivot = low;// 中枢元素位置,我们以第一个元素为中枢元素

		// 退出条件这里只可能是low = high

		while (true) {

			if (pivot != high) {// 如果中枢元素在低指针位置时,我们移动高指针

				// 如果高指针元素小于中枢元素时,则与中枢元素交换

				if ((array[high].compareTo(array[pivot])) < 0) {

					swap(array, high, pivot);

					// 交换后中枢元素在高指针位置了

					pivot = high;

				} else {// 如果未找到小于中枢元素,则高指针前移继续找

					high--;

				}

			} else {// 否则中枢元素在高指针位置

				// 如果低指针元素大于中枢元素时,则与中枢元素交换

				if ((array[low].compareTo(array[pivot])) > 0) {

					swap(array, low, pivot);

					// 交换后中枢元素在低指针位置了

					pivot = low;

				} else {// 如果未找到大于中枢元素,则低指针后移继续找

					low++;

				}

			}

			if (low == high) {

				break;

			}

		}

		// 返回中枢元素所在位置,以便下次分区

		return pivot;

	}

	private int partition3(Integer[] array, int low, int high) {

		int pivot = low;// 中枢元素位置,我们以第一个元素为中枢元素

		low++;

		// ----调整高低指针所指向的元素顺序,把小于中枢元素的移到前部分,大于中枢元素的移到后面部分

		// 退出条件这里只可能是low = high

		while (true) {

			// 如果高指针未超出低指针

			while (low < high) {
				// 如果低指针指向的元素大于或等于中枢元素时表示找到了,退出,注:等于时也要后移
				if ((array[low].compareTo(array[pivot])) >= 0) {
					break;
				} else {// 如果低指针指向的元素小于中枢元素时继续找
					low++;
				}
			}

			while (high > low) {

				// 如果高指针指向的元素小于中枢元素时表示找到,退出

				if ((array[high].compareTo(array[pivot])) < 0) {

					break;

				} else {// 如果高指针指向的元素大于中枢元素时继续找

					high--;

				}

			}

			// 退出上面循环时low = high

			if (low == high) {

				break;

			}

			swap(array, low, high);

		}

		// ----高低指针所指向的元素排序完成后,还得要把中枢元素放到适当的位置

		if ((array[pivot].compareTo(array[low])) > 0) {

			// 如果退出循环时中枢元素大于了低指针或高指针元素时,中枢元素需与low元素交换

			swap(array, low, pivot);

			pivot = low;

		} else if ((array[pivot].compareTo(array[low])) <= 0) {

			swap(array, low - 1, pivot);

			pivot = low - 1;

		}

		// 返回中枢元素所在位置,以便下次分区

		return pivot;

	}

	public void swap(Integer[] array, int i, int j) {

		if (i != j) {// 只有不是同一位置时才需交换

			Integer tmp = array[i];

			array[i] = array[j];

			array[j] = tmp;

		}

	}

}

 
 
 
 
 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值