选择排序

选择排序有简单选择排序,堆排序

简单选择排序:

import java.util.Arrays;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        int[] array = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3};
        System.out.println(Arrays.toString(array));
        selectionSort(array);
        System.out.println(Arrays.toString(array));
    }

    //选择排序
    public static void selectionSort(int[] array){
        int minPoint;  //存储最小元素的小标
        int len = array.length;
        int temp;
        int counter = 1;

        for(int i=0;i<len-1;i++){

            minPoint= i;
            for(int j=i+1;j<=len-1;j++){  //每完成一轮排序,就确定了一个相对最小元素,下一轮排序只对后面的元素排序
                if(array[j]<array[minPoint]){  //如果待排数组中的某个元素比当前元素小,minPoint指向该元素的下标
                    minPoint= j;
                }
            }

            if(minPoint!=i){  //如果发现了更小的元素,交换位置
                temp= array[i];
                array[i]= array[minPoint];
                array[minPoint]= temp;
            }

            System.out.print("第"+counter+"轮排序结果:");
            display(array);
            counter++;
        }
    }

    private static void display(int[] array){
        System.out.println(Arrays.toString(array));
    }
}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3]
第1轮排序结果:[-3, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, 9]
第2轮排序结果:[-3, -2, 7, 6, 5, 4, 3, 2, 1, 0, -1, 8, 9]
第3轮排序结果:[-3, -2, -1, 6, 5, 4, 3, 2, 1, 0, 7, 8, 9]
第4轮排序结果:[-3, -2, -1, 0, 5, 4, 3, 2, 1, 6, 7, 8, 9]
第5轮排序结果:[-3, -2, -1, 0, 1, 4, 3, 2, 5, 6, 7, 8, 9]
第6轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第7轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第8轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第9轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第10轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第11轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
第12轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
View Code

简单选择排序,就是一次遍历选择最小的,然后与第一位进行交换,然后再剩下的里面再次进行查找最小的,再次交换........

选择排序与冒泡排序一样,需要进行N*(N-1)/2次比较,但是只需要N次交换,当N很大时,交换次数的时间影响力更大,所以选择排序的时间复杂度为O(N2)

虽然选择排序与冒泡排序在时间复杂度属于同一量级,但是毫无疑问选择排序的效率更高,因为它的交换操作次数更少,而且在交换操作比比较操作的时间级大得多时,选择排序的速度是相当快的。

优化方案:一次遍历查找出最小跟最大值,分别与第一位,最后一位进行交换,然后在剩下的数组里面再次进行查找最大,最小........

import java.util.Arrays;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        int[] array = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3};
        System.out.println(Arrays.toString(array));
        selectionSort_improvement(array);
        System.out.println(Arrays.toString(array));
    }
    
    //选择排序改进版
    public static void selectionSort_improvement(int[] array){
        int minPoint;  //存储最小元素的小标
        int maxPoint;  //存储最大元素的小标
        int len = array.length;
        int temp;
        int counter = 1;

        for(int i=0;i<len/2;i++){

            minPoint= i;
            maxPoint= i;
            for(int j=i+1;j<=len-1-i;j++){  //每完成一轮排序,就确定了两个最值,下一轮排序时比较范围减少两个元素
                if(array[j]<array[minPoint]){  //如果待排数组中的某个元素比当前元素小,minPoint指向该元素的下标
                    minPoint= j;
                    continue;
                }else if(array[j]>array[maxPoint]){  //如果待排数组中的某个元素比当前元素大,maxPoint指向该元素的下标
                    maxPoint= j;
                }
            }

            if(minPoint!=i){  //如果发现了更小的元素,与第一个元素交换位置
                temp= array[i];
                array[i]= array[minPoint];
                array[minPoint]= temp;

                //原来的第一个元素已经与下标为minPoint的元素交换了位置
                //如果之前maxPoint指向的是第一个元素,那么需要将maxPoint重新指向array[minPoint]
                //因为现在array[minPoint]存放的才是之前第一个元素中的数据
                if(maxPoint== i){
                    maxPoint= minPoint;
                }
            }
            if(maxPoint!=len-1-i){  //如果发现了更大的元素,与最后一个元素交换位置
                temp= array[len-1-i];
                array[len-1-i]= array[maxPoint];
                array[maxPoint]= temp;
            }
            System.out.print("第"+counter+"轮排序结果:");
            display(array);
            counter++;
        }
    }

    private static void display(int[] array){
        System.out.println(Arrays.toString(array));
    }
}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3]
第1轮排序结果:[-3, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, 9]
第2轮排序结果:[-3, -2, 7, 6, 5, 4, 3, 2, 1, 0, -1, 8, 9]
第3轮排序结果:[-3, -2, -1, 6, 5, 4, 3, 2, 1, 0, 7, 8, 9]
第4轮排序结果:[-3, -2, -1, 0, 5, 4, 3, 2, 1, 6, 7, 8, 9]
第5轮排序结果:[-3, -2, -1, 0, 1, 4, 3, 2, 5, 6, 7, 8, 9]
第6轮排序结果:[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
View Code

堆排序:

这个版本感觉不太好

import java.util.Arrays;
import java.util.Random;

public class Main {
    static  int[] array = {7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2,10,-3};
    public static void main(String[] args) throws InterruptedException {

        System.out.println(Arrays.toString(array));
        heapSort();
        System.out.println(Arrays.toString(array));
    }


    //堆排序
    public static void heapSort(){
        buildHeap();
        System.out.println("建堆:");
        printTree(array.length);

        int lastIndex = array.length-1;
        while(lastIndex>0){
            swap(0,lastIndex);  //取出堆顶元素,将堆底放入堆顶。其实就是交换下标为0与lastIndex的数据
            if(--lastIndex == 0) break;  //只有一个元素时就不用调整堆了,排序结束
            adjustHeap(0,lastIndex);  //调整堆

            System.out.println("调整堆:");
            printTree(lastIndex+1);
        }

    }



    /**
     * 用数组中的元素建堆
     */
    private static void buildHeap(){
        int lastIndex = array.length-1;
        for(int i= (lastIndex-1)/2;i>=0;i--){ //(lastIndex-1)/2就是最后一个元素的根节点的下标,依次调整每棵子树
            adjustHeap(i,lastIndex);  //调整以下标i的元素为根的子树
        }
    }

    /**
     * 调整以下标是rootIndex的元素为根的子树
     *@param rootIndex 根的下标
     *@param lastIndex 堆中最后一个元素的下标
     */
    private static void adjustHeap(int rootIndex,int lastIndex){

        int biggerIndex = rootIndex;
        int leftChildIndex = 2*rootIndex+1;
        int rightChildIndex = 2*rootIndex+2;

        if(rightChildIndex<=lastIndex){  //存在右子节点,则必存在左子节点

            if(array[rootIndex]<array[leftChildIndex] || array[rootIndex]<array[rightChildIndex]){ //子节点中存在比根更大的元素
                biggerIndex = array[leftChildIndex]<array[rightChildIndex] ? rightChildIndex :leftChildIndex;
            }

        }else if(leftChildIndex<=lastIndex){  //只存在左子节点

            if(array[leftChildIndex]>array[rootIndex]){  //左子节点更大
                biggerIndex = leftChildIndex;
            }
        }

        if(biggerIndex != rootIndex){  //找到了比根更大的子节点

            swap(rootIndex,biggerIndex);

            //交换位置后可能会破坏子树,将焦点转向交换了位置的子节点,调整以它为根的子树
            adjustHeap(biggerIndex,lastIndex);
        }
    }

    /**
     * 将数组按照完全二叉树的形式打印出来
     */
    private static void printTree(int len){

        int layers = (int)Math.floor(Math.log((double)len)/Math.log((double)2))+1;  //树的层数
        int maxWidth = (int)Math.pow(2,layers)-1;  //树的最大宽度
        int endSpacing = maxWidth;
        int spacing;
        int numberOfThisLayer;
        for(int i=1;i<=layers;i++){  //从第一层开始,逐层打印
            endSpacing = endSpacing/2;  //每层打印之前需要打印的空格数
            spacing = 2*endSpacing+1;  //元素之间应该打印的空格数
            numberOfThisLayer = (int)Math.pow(2, i-1);  //该层要打印的元素总数

            int j;
            for(j=0;j<endSpacing;j++){
                System.out.print("  ");
            }

            int beginIndex = (int)Math.pow(2,i-1)-1;  //该层第一个元素对应的数组下标
            for(j=1;j<=numberOfThisLayer;j++){
                System.out.print(array[beginIndex++]+"");
                for(int k=0;k<spacing;k++){  //打印元素之间的空格
                    System.out.print("  ");
                }
                if(beginIndex == len){  //已打印到最后一个元素
                    break;
                }
            }

            System.out.println();
        }
        System.out.println();
    }

    private static void swap(int low,int high){
        int temp = array[high];
        array[high] = array[low];
        array[low] = temp;
    }
}
View Code
[7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2, 10, -3]
建堆:
              10                              
      8              9              
  6      1      7      3      
2  5  0  -1  -2  4  -3  

调整堆:
              9                              
      8              7              
  6      1      4      3      
2  5  0  -1  -2  -3  

调整堆:
              8                              
      6              7              
  5      1      4      3      
2  -3  0  -1  -2  

调整堆:
              7                              
      6              4              
  5      1      -2      3      
2  -3  0  -1  

调整堆:
              6                              
      5              4              
  2      1      -2      3      
-1  -3  0  

调整堆:
              5                              
      2              4              
  0      1      -2      3      
-1  -3  

调整堆:
              4                              
      2              3              
  0      1      -2      -3      
-1  

调整堆:
      3              
  2      -1      
0  1  -2  -3  

调整堆:
      2              
  1      -1      
0  -3  -2  

调整堆:
      1              
  0      -1      
-2  -3  

调整堆:
      0              
  -2      -1      
-3  

调整堆:
  -1      
-2  -3  

调整堆:
  -2      
-3  

[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
View Code

堆排序的特性:

一个长度为n的数组,可以看成是一个array[0,......,n-1]的完全二叉树。

子节点索引为n-1的二叉树,其父节点的索引为(n-2)/2。

父节点索引为i的二叉树,其子节点的索引分别为2*i+1和2*i+2。

模板:

import java.util.Arrays;

public class Main {
    static  int[] array = {7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2,10,-3};
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Arrays.toString(array));
        heapSort(array);
        System.out.println(Arrays.toString(array));
    }
    
    //堆排序
    public static int[] heapSort(int[] array){
        array = buildMaxHeap(array); //初始建堆,array[0]为第一趟值最大的元素
        for(int i=array.length-1;i>1;i--){
            int temp = array[0];  //将堆顶元素和堆低元素交换,即得到当前最大元素正确的排序位置
            array[0] = array[i];
            array[i] = temp;
            adjustDownToUp(array, 0,i);  //整理,将剩余的元素整理成堆
        }
        return array;
    }

    //构建大根堆:将array看成完全二叉树的顺序存储结构
    private static  int[] buildMaxHeap(int[] array){
        //从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,反复调整堆
        for(int i=(array.length-2)/2;i>=0;i--){
            adjustDownToUp(array, i,array.length);
        }
        return array;
    }

    //将元素array[k]自下往上逐步调整树形结构
    private static void adjustDownToUp(int[] array,int k,int length){
        int temp = array[k];
        for(int i=2*k+1; i<length-1; i=2*i+1){    //i为初始化为节点k的左孩子,沿节点较大的子节点向下调整(即选择节点i中较大的子节点的左孩子为新的i,有点绕)
            if(i<length && array[i]<array[i+1]){  //取节点较大的子节点的下标
                i++;   //如果节点的右孩子>左孩子,则取右孩子节点的下标
            }
            if(temp>=array[i]){  //根节点 >=左右子女中关键字较大者,调整结束
                break;
            }else{   //根节点 <左右子女中关键字较大者
                array[k] = array[i];  //将左右子结点中较大值array[i]调整到双亲节点上
                k = i; //【关键】修改k值,以便继续向下调整
            }
        }
        array[k] = temp;  //被调整的结点的值放人最终位置
    }
}
[7, 8, 9, 6, 1, 4, 3, 2, 5, 0, -1, -2, 10, -3]
[-2, -3, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
View Code

看堆时,发现最大的k个数,肯定在堆的前k排,比如第二大的数,肯定在第二排,不是根节点的左孩子,就是根节点的右孩子,按这种方式,可以发现堆排序有很大的优化空间。

http://blog.csdn.net/u012152619/article/details/47306053

http://blog.csdn.net/u012152619/article/details/47452813

http://www.cnblogs.com/CherishFX/p/4643940.html

原文地址:https://www.cnblogs.com/hongdada/p/6171377.html