黑马程序员--------Java排序总结

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

排序

  排序分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中要使用外存,则称为外排序。 
 (1)、插入排序:直接插入排序、二分法插入排序、希尔排序。
 (2)、选择排序:简单选择排序、堆排序。
 (3)、交换排序:冒泡排序、快速排序。
 (4)、归并排序 
 (5)、基数排序

一、插入排序  

  •思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止。
  •关键问题:在前面已经排好序的序列中找到合适的插入位置。
  •方法:–直接插入排序
          –二分插入排序
         –希尔排序
  1直接插入排序(从后向前找到合适位置后插入)
     1)、基本思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
   2)、分析
     直接插入排序是稳定的排序。文件初态不同时,直接插入排序所耗费的时间有很大差异。若文件初态为正序,则每个待插入的记录只需要比较一次就能够找到。合适的位置插入,故算法的时间复杂度为O(n),这时最好的情况。若初态为反序,则第i个待插入记录需要比较i+1次才能找到。直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
3)、java代码实现
public class Demo {

	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        straitSort(a);
        System.out.println("直接插入排序:"+Arrays.toString(a));
	}
	public static void straitSort(int[] a){
		for (int i = 1; i < a.length; i++) {
			int temp=a[i];
			int j;
			for(j=i-1;j>=0;j--){
				if(a[j]>temp){
					a[j+1]=a[j];
				}else{
				break;
				}
			}
			a[j+1]=temp;
		}
	}

}

2二分法插入排序 (按二分法找到合适位置插入)
    1)、基本思想:在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到left>right,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。二分法没有排序,只有查找。所以当找到要插入的位置时。移动必须从最后一个记录开始,向后移动一位,再移动倒数第2位,直到要插入的位置的记录移后一位。
2)、分析
二分插入排序是稳定的与二分查找的复杂度相同;最好的情况是当插入的位置刚好是二分位置 所用时间为O(n);最坏的情况是当插入的位置不在二分位置,所需比较次数为n
3)、java实现
public class Demoerfen {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        dSort(a);
        System.out.println("二分法插入排序:"+Arrays.toString(a));
	}
	public static void dSort(int[] a){
        for (int i = 0; i < a.length; i++) {
            int temp = a[i];
            int left = 0;
            int right = i - 1;
            int mid = 0;
            while (left <= right) {
                mid = (left + right) / 2;
                if (temp < a[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            for (int j = i - 1; j >= left; j--) {
                a[j + 1] = a[j];
            }
            if (left != i) {
                a[left] = temp;
            }
        }
	}
	
}
3希尔排序
1)、基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录
放在同一个组中。 先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至
 所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
2)、分析
我们知道一次插入排序是稳定的,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会
被打乱,所以希尔排序是不稳定的。
希尔排序的时间性能优于直接插入排序,原因如下:
      (1)当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
      (2)当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
      (3)在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,
            分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,
            所以新的一趟排序过程也较快。因此,希尔排序在效率上较直接插人排序有较大的改进。希尔排序的平均时间复杂度为O(nlogn)。
3)、java实现
public class Demoxier {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        shellSort(a);
        System.out.println("希尔排序:"+Arrays.toString(a));
	}
	 public static void shellSort(int[] a) {
	        int d = a.length;
	        while (true) {
	            d = d / 2;
	            for (int x = 0; x < d; x++) {
	                for (int i = x + d; i < a.length; i = i + d) {
	                    int temp = a[i];
	                    int j;
	                    for (j = i - d; j >= 0 && a[j] > temp; j = j - d) {
	                        a[j + d] = a[j];
	                    }
	                    a[j + d] = temp;
	                }
	            }
	            if (d == 1) {
	                break;
	            }
	        }
	    }
}

二、选择排序 

     •思想:每趟从待排序的记录序列中选择关键字最小的记录放置到已排序表的最前位置,直到全部排完。
    •关键问题:在剩余的待排序记录序列中找到最小关键码记录。
     •方法: –直接选择排序 
            –堆排序
      
 
1简单的选择排序 
1)、基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的
                     与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
2)、分析
        简单选择排序是不稳定的排序。
        时间复杂度:T(n)=O(n2)。
        3)、java实现
public class Demoxuanze {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        selectSort(a);
        System.out.println("简单选择排序:"+Arrays.toString(a));
	}
	 public static void selectSort(int[] a) {
	        for (int i = 0; i < a.length; i++) {
	            int min = a[i];
	            int n = i; 
	            for (int j = i + 1; j < a.length; j++) {
	                if (a[j] < min) { 
	                    min = a[j];
	                    n = j;
	                }
	            }
	            a[n] = a[i];
	            a[i] = min;
	 
	        }
	    }
}
2堆排序
       1)、基本思想:
        堆排序是一种树形选择排序,是对直接选择排序的有效改进。
        堆的定义下:具有n个元素的序列(h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)
          (i=1,2,...,n/2)时称之为堆。
          在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。
          完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
        思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。
          然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对
          它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个
          元素交换位置。所以堆排序有两个函数组成一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
2)、分析
        堆排序也是一种不稳定的排序算法。
        堆排序优于简单选择排序的原因:
        直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,
          又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时
          未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。堆排序可通过树形结构保存部分比较结果,可减少比较次数。
        堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。由于建初始堆所需的比较次数较多,
          所以堆排序不适宜于记录数较少的文件。
       3)、java实现
public class Demodui {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        heapSort(a);
        System.out.println("堆排序:"+Arrays.toString(a));
	}
	 public static void heapSort(int[] a ) {
	        int arrayLength = a.length;
	  
	        for (int i = 0; i < arrayLength - 1; i++) {
	            buildMaxHeap(a, arrayLength - 1 - i);
	            swap(a, 0, arrayLength - 1 - i);
	        }
	    }
	 private static void buildMaxHeap(int[] data, int lastIndex) {
	        for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
	            int k = i;
	            while (k * 2 + 1 <= lastIndex) {
	                int biggerIndex = 2 * k + 1;
	                if (biggerIndex < lastIndex) {
	                    if (data[biggerIndex] < data[biggerIndex + 1]) {
	                        biggerIndex++;
	                    }
	                }
	                if (data[k] < data[biggerIndex]) {
	                    swap(data, k, biggerIndex);
	                    k = biggerIndex;
	                } else {
	                    break;
	                }
	            }
	        }
	    }
	    private static void swap(int[] data, int i, int j) {
	        int tmp = data[i];
	        data[i] = data[j];
	        data[j] = tmp;
	    }
}

 三、交换排序

     1、冒泡排序
1)、基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,
               让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。 
2)、分析
        冒泡排序是一种稳定的排序方法。 
         •若文件初状为正序,则一趟起泡就可完成排序,排序码的比较次数为n-1,且没有记录移动,时间复杂度是O(n)
         •若文件初态为逆序,则需要n-1趟起泡,每趟进行n-i次排序码的比较,且每次比较都移动三次,
          比较和移动次数均达到最大值∶O(n2)
         •起泡排序平均时间复杂度为O(n2)
3)、Java实现
public class Demomaopao {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        bubbleSort(a);
        System.out.println("冒泡排序:"+Arrays.toString(a));
	}
    public static void bubbleSort(int[] a) {
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a.length - i - 1; j++) {
                if (a[j] > a[j + 1]) {
                    int temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                }
            }
        }
    }
}

 2、快速排序

1)、基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,
                       一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用
                      同样的方法递归地排序划分的两部分。 
2)、分析
        快速排序是不稳定的排序。
        快速排序的时间复杂度为O(nlogn)。
        当n较大时使用快排比较好,当序列基本有序时用快排反而不好。
3)、Java实现
public class Demokuaisu {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        quickSort(a);
        System.out.println("快速排序:"+Arrays.toString(a));
	}
	 public static void quickSort(int[] a) {
	        if (a.length > 0) {
	            quickSort(a, 0, a.length - 1);
	        }
	    }
	    private static void quickSort(int[] a, int low, int high) {
	        if (low < high) { 
	            int middle = getMiddle(a, low, high);
	            quickSort(a, 0, middle - 1);
	            quickSort(a, middle + 1, high);
	        }
	    }
	    private static int getMiddle(int[] a, int low, int high) {
	        int temp = a[low];
	        while (low < high) {
	            
	            while (low < high && a[high] >= temp) {
	                high--;
	            }
	            a[low] = a[high];
	            while (low < high && a[low] <= temp) {
	                low++;
	            }
	            a[high] = a[low];
	        }
	        a[low] = temp;
	        return low;
	    }
}

四、归并排序

       1、基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表。
          即把待排序序列分为若干个子序列,每个子序列是有序的。
          然后再把有序子序列合并为整体有序序列。 
      2、分析
        归并排序是稳定的排序方法。
        归并排序的时间复杂度为O(nlogn)。
        速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
3、Java实现
public class Demoguina {
	public static void main(String[] args) {
        int[] a = {4, 3, 6, 9, 7, 1, 2, 5, 8};
        System.out.println("排序前:"+Arrays.toString(a));
        gbSort(a);
        System.out.println("归纳排序:"+Arrays.toString(a));
	}
	   public static void gbSort(int[] a) {

	        mergeSort(a, 0, a.length - 1);
	    }
	    private static void mergeSort(int[] a, int left, int right) {
	        if (left < right) {
	            int middle = (left + right) / 2;
	      
	            mergeSort(a, left, middle);
	           
	            mergeSort(a, middle + 1, right);
	           
	            merge(a, left, middle, right);
	        }
	    }
	    private static void merge(int[] a, int left, int middle, int right) {
	        int[] tmpArr = new int[a.length];
	        int mid = middle + 1; 
	        int tmp = left;
	        int third = left;
	        while (left <= middle && mid <= right) {
	            if (a[left] <= a[mid]) {
	                tmpArr[third++] = a[left++];
	            } else {
	                tmpArr[third++] = a[mid++];
	            }
	        }
	        while (left <= middle) {
	            tmpArr[third++] = a[left++];
	        }
	        while (mid <= right) {
	            tmpArr[third++] = a[mid++];
	        }
	        while (tmp <= right) {
	            a[tmp] = tmpArr[tmp++];
	        }
	    }
}<span style="font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 1em; background-color: rgb(255, 255, 255);">  </span>

五、基数排序

1、基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。 
2、分析
        基数排序是稳定的排序算法。
        基数排序的时间复杂度为O(d(n+r)),d为位数,r为基数。
3、Java实现
   public static void baseSort(int[] a ) {
        int max = 0;
        for (int i = 0; i < a.length; i++) {
            if (max < a[i]) {
                max = a[i];
            }
        }
        int times = 0;
        while (max > 0) {
            max = max / 10;
            times++;
        }
        List<ArrayList> queue = new ArrayList<ArrayList>();
        for (int i = 0; i < 10; i++) {
            ArrayList queue1 = new ArrayList();
            queue.add(queue1);
        }
        for (int i = 0; i < times; i++) { 
            for (int j = 0; j < a.length; j++) {
                int x = a[j] % (int) Math.pow(10, i + 1)
                        / (int) Math.pow(10, i);
                ArrayList queue2 = queue.get(x);
                queue2.add(a[j]);
                queue.set(x, queue2);
            }
            int count = 0;
            for (int j = 0; j < 10; j++) {
                while (queue.get(j).size() > 0) {
                    ArrayList<Integer> queue3 = queue.get(j);
                    a[count] = queue3.get(0);
                    queue3.remove(0);
                    count++;
                }
            }
        }
    }

总结:    

      一、稳定性:   
        稳定:冒泡排序、插入排序、归并排序和基数排序 
        不稳定:选择排序、快速排序、希尔排序、堆排序
      二、平均时间复杂度
        O(n^2):直接插入排序,简单选择排序,冒泡排序。 
        在数据规模较小时(9W内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为O(n^2)
      的算法基本上是相邻元素进行比较,基本上都是稳定的。
       O(nlogn):快速排序,归并排序,希尔排序,堆排序。
        其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。  
      三、排序算法的选择
        1.数据规模较小
        (1)待排序列基本序的情况下,可以选择直接插入排序;
        (2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
        2.数据规模不是很大
       (1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
        (2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
        3.数据规模很大
        (1)对稳定性有求,则可考虑归并排序。
        (2)对稳定性没要求,宜用堆排序
        4.序列初始基本有序(正序),宜用直接插入,冒泡
     
      





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值