zoukankan      html  css  js  c++  java
  • 选择排序(C++/Java实现)

    常用的选择排序算法有两种:直接选择排序和堆排序。 

    一、直接选择排序

    基本思路:

    第一趟比较:程序将记录定位在第一个数据上,拿第一个数据依次和后面的数据进行比较,如果第一个数据大于后面的某个数据,交换它们,....依次进行下去。这趟比较将选出最小的数据并将其排在第一位。

    第二趟比较:程序将记录定位在第二个数据上,拿第二个数据依次和后面的数据进行比较,如果第二个数据大于后面的某个数据,交换它们,....依次进行下去。这趟比较将选出第二小的数据并将其排在第二位。

    ......

    依此规则进行比较n-1趟,这组数据中第2大的数据被选出之后,被排在倒数第1位,剩下的就是最大的数了。

    Java实现代码:

    //定义一个数据包装类  
    public class DataWrap implements Comparable<DataWrap>    
    {  
        int data;  
        String flag;  
        public DataWrap(int data, String flag)  
        {  
            this.data = data;  
            this.flag = flag;  
        }  
        public String toString()  
        {  
            return data + flag;  
        }  
        //根据data实例变量来决定两个DataWrap的大小  
        public int compareTo(DataWrap dw)  
        {  
            return this.data > dw.data ? 1   
                : (this.data == dw.data ? 0 : -1);  
        }  
    }  

    直接选择排序:

    //选择排序
    public class SelectSort  
    {  
        public static void selectSort(DataWrap[] data)   
        {  
            System.out.println("开始排序");  
            int arrayLength = data.length;  
            //依次进行n-1趟比较, 第i趟比较将第i大的值选出放在i位置上。  
            for (int i = 0; i < arrayLength - 1 ; i++ )  
            {  
                //第i个数据只需和它后面的数据比较  
                for (int j = i + 1 ; j < arrayLength ; j++ )  
                {                 
                    //如果第i位置的数据 > j位置的数据, 交换它们  
                    if (data[i].compareTo(data[j]) > 0)  
                    {  
                        DataWrap tmp = data[i];  
                        data[i] = data[j];  
                        data[j] = tmp;  
                    }  
                }  
                System.out.println(java.util.Arrays.toString(data));  
            }  
        }  
        public static void main(String[] args)  
        {  
            DataWrap[] data = {  
                new DataWrap(21 , ""),  
                new DataWrap(30 , ""),  
                new DataWrap(49 , ""),  
                new DataWrap(30 , "*"),  
                new DataWrap(16 , ""),  
                new DataWrap(9 , "")  
            };  
            System.out.println("排序之前:\n"  
                + java.util.Arrays.toString(data));  
            selectSort(data);  
            System.out.println("排序之后:\n"   
                + java.util.Arrays.toString(data));  
        }  
    }  

    下面是排序过程:

    排序之前:
    [21, 30, 49, 30*, 16, 9]
    开始排序
    [9, 30, 49, 30*, 21, 16]
    [9, 16, 49, 30*, 30, 21]
    [9, 16, 21, 49, 30, 30*]
    [9, 16, 21, 30, 49, 30*]
    [9, 16, 21, 30, 30*, 49]
    排序之后:
    [9, 16, 21, 30, 30*, 49]

    对上面的算法改进,每次找到最小的数据的索引,减少交换的次数,提高算法效率: 

    public class SelectSort2  
    {  
        public static void selectSort(DataWrap[] data)   
        {  
            System.out.println("开始排序");  
            int arrayLength = data.length;  
            //依次进行n-1趟比较, 第i趟比较将第i大的值选出放在i位置上。  
            for (int i = 0; i < arrayLength - 1 ; i++ )  
            {  
                //minIndex永远保留本趟比较中最小值的索引  
                int minIndex = i ;  
                //第i个数据只需和它后面的数据比较  
                for (int j = i + 1 ; j < arrayLength ; j++ )  
                {  
                    //如果第minIndex位置的数据 > j位置的数据  
                    if (data[minIndex].compareTo(data[j]) > 0)  
                    {  
                        //将j的值赋给minIndex  
                        minIndex = j;  
                    }  
                }  
                //每趟比较最多交换一次  
                if (minIndex != i)  
                {  
                    DataWrap tmp = data[i];  
                    data[i] = data[minIndex];  
                    data[minIndex] = tmp;  
                }  
                System.out.println(java.util.Arrays.toString(data));  
            }  
        }  
        public static void main(String[] args)  
        {  
            DataWrap[] data = {  
                new DataWrap(21 , ""),  
                new DataWrap(30 , ""),  
                new DataWrap(49 , ""),  
                new DataWrap(30 , "*"),  
                new DataWrap(16 , ""),  
                new DataWrap(9 , "")  
            };  
            System.out.println("排序之前:\n"  
                + java.util.Arrays.toString(data));  
            selectSort(data);  
            System.out.println("排序之后:\n"   
                + java.util.Arrays.toString(data));  
        }  
    }  

    下面是排序过程:

    排序之前:
    [21, 30, 49, 30*, 16, 9]
    开始排序
    [9, 30, 49, 30*, 16, 21]
    [9, 16, 49, 30*, 30, 21]
    [9, 16, 21, 30*, 30, 49] 不交换
    [9, 16, 21, 30*, 30, 49] 不交换
    [9, 16, 21, 30*, 30, 49]
    排序之后:
    [9, 16, 21, 30*, 30, 49]

    可以看出:直接选择排序的第n趟比较至多交换一次,永远总是拿n-1位的数据和中间某个数据(本趟比较中最小的数据)进行交换。如果本趟比较时第n-1位(本趟比较的第一位)数据已经是最小,则无需进行交换。

     C++代码实现:

    #include<iostream>
    using namespace std;
    
    //直接选择排序
    void select_sort(int a[],int len)
    {
    
        int i,j,x,l;
        for(i=0;i<len;i++)
        {
        
            x = a[i];//每次遍历前对x和i的初值设定
            l = i;
    
            for(j=i;j<len;j++)//遍历从i位置向数组尾部进行
            {
            
                if(a[j] < x)
                {            
    
                    x = a[j];//x保存每次遍历搜索到的最小数
                    l = j;//l记录最小数的位置
                }
            }
            a[l] = a[i];//把最小数与a[i]交换
            a[i] = x;
        }
    }
    
    void main()
    {
    
        int data[9] = {54,38,96,23,15,72,60,45,83};
        select_sort(data,9);
        for(int i=0;i<9;i++)
        {
        
            cout<<data[i]<<" ";
        }
    }

    二、堆排序

    首先,关于堆的概念这里就不说了,详情请看《算法导论》

    堆排序的关键在于建堆,下面简单介绍一下建堆的过程:

    第1趟将索引0至n-1处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的最后一个节点交换,就使的这组数据中最大(最小)值排在了最后。

    第2趟将索引0至n-2处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的倒数第二个节点交换,就使的这组数据中最大(最小)值排在了倒数第二位。

    ......

    第k趟将索引0至n-k处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的倒数第k个节点交换,就使的这组数据中最大(最小)值排在了倒数第k位。

    所以我们只是在重复做两件事:

    1:建堆

    2:拿堆的根节点和最后一个节点交换

    下面通过一组数据说明:

    9,79,46,30,58,49

    1:先将其转换为完全二叉树,如图

    2:完全二叉树的最后一个非叶子节点,也就是最后一个节点的父节点。最后一个节点的索引为数组长度len-1,那么最后一个非叶子节点的索引应该是为(len-1)/2.也就是从索引为2的节点开始,如果其子节点的值大于其本身的值。则把他和较大子节点进行交换,即将索引2处节点和索引5处元素交换。交换后的结果如图:

    建堆从最后一个非叶子节点开始即可

    3:向前处理前一个节点,也就是处理索引为1的节点,此时79>30,79>58,因此无需交换。

    4:向前处理前一个节点,也就是处理索引为0的节点,此时9<79,9<49,因此无需交换。应该拿索引为0的节点与索引为1的节点交换,因为79>49.如图

    5:如果某个节点和它的某个子节点交换后,该子节点又有子节点,系统还需要再次对该子节点进行判断。如上图因为1处,3处,4处中,1处的值大于3,4出的值,所以还要交换。

    Java代码实现:

    //堆排序
    public class HeapSort  
    {  
        public static void heapSort(DataWrap[] data)   
        {  
            System.out.println("开始排序");  
            int arrayLength = data.length;  
            //循环建堆  
            for (int i = 0; i < arrayLength - 1 ; i++ )  
            {  
                //建堆  
                builMaxdHeap(data , arrayLength - 1 - i);  
                //交换堆顶和最后一个元素  
                swap(data , 0 , arrayLength - 1 - i);  
                System.out.println(java.util.Arrays.toString(data));  
            }  
        }  
        //对data数组从0到lastIndex建大顶堆  
        private static void builMaxdHeap(DataWrap[] data , int lastIndex)  
        {  
            //从lastIndex处节点(最后一个节点)的父节点开始  
            for (int i = (lastIndex - 1) / 2 ; i >= 0  ; i--)  
            {  
                //k保存当前正在判断的节点  
                int k = i;  
                //如果当前k节点的子节点存在  
                while (k * 2 + 1 <= lastIndex)  
                {  
                    //k节点的左子节点的索引  
                    int biggerIndex = 2 * k  + 1;   
                    //如果biggerIndex小于lastIndex,即biggerIndex + 1  
                    //代表的k节点的右子节点存在  
                    if (biggerIndex < lastIndex)  
                    {  
                         //如果右子节点的值较大  
                        if(data[biggerIndex].compareTo(data[biggerIndex + 1]) < 0)  
                        {  
                            //biggerIndex总是记录较大子节点的索引  
                            biggerIndex++;   
                        }  
                    }  
                    //如果k节点的值小于其较大子节点的值  
                    if(data[k].compareTo(data[biggerIndex]) < 0)  
                    {  
                        //交换它们  
                        swap(data , k , biggerIndex);  
                        //将biggerIndex赋给k,开始while循环的下一次循环,  
                        //重新保证k节点的值大于其左、右子节点的值。  
                        k = biggerIndex;  
                    }  
                    else  
                    {  
                        break;  
                    }  
                }  
            }  
        }  
        //交换data数组中i、j两个索引处的元素  
        private static void swap(DataWrap[] data , int i , int j)  
        {  
            DataWrap tmp = data[i];  
            data[i] = data[j];  
            data[j] = tmp;  
        }  
        public static void main(String[] args)  
        {  
            DataWrap[] data = {  
                new DataWrap(21 , ""),  
                new DataWrap(30 , ""),  
                new DataWrap(49 , ""),  
                new DataWrap(30 , "*"),  
                new DataWrap(21 , "*"),  
                new DataWrap(16 , ""),  
                new DataWrap(9 , "")  
            };  
            System.out.println("排序之前:\n"  
                + java.util.Arrays.toString(data));  
            heapSort(data);  
            System.out.println("排序之后:\n"   
                + java.util.Arrays.toString(data));  
        }  
    }  

    实现过程:

    排序之前:
    [21, 30, 49, 30*, 21*, 16, 9]
    开始排序
    [9, 30, 21, 30*, 21*, 16, 49]
    [16, 30*, 21, 9, 21*, 30, 49]
    [16, 21*, 21, 9, 30*, 30, 49]
    [9, 16, 21, 21*, 30*, 30, 49]
    [9, 16, 21, 21*, 30*, 30, 49]
    [9, 16, 21, 21*, 30*, 30, 49]
    排序之后:
    [9, 16, 21, 21*, 30*, 30, 49]

    C++实现:

    #include<iostream>
    using namespace std;
    
    int heapSize = 0;
    
    void swap(int array[] , int i , int j)
    {
        int temp;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    //array[index]与其左右子树进行递归对比,用最大值替换array[index],index表示堆顶索引
    void MaxHeapify(int array[] , int index )
    {
        int left = 2*index;//左子节点索引
        int right = 2*index+1;//右子节点索引
        int largest;//最大数
        //用largest存储堆顶与其左子节点的较大者
        if(left<=heapSize && array[left]>array[index])
            largest = left;
        else
            largest = index;
        //用largest存储堆顶与其右子节点的较大者
        if(right<=heapSize && array[right]>array[largest])
            largest = right;
        //此时largest为堆顶,左子节点,右子节点最大者
        if(largest != index)
        {
            //如果堆顶不是最大者,则交换,并递归调整堆
            swap(array , index , largest);
            MaxHeapify(array , largest);
        }
    }
     
    //初始化堆,将数组中的每一个元素放到合适的位置。此时堆顶的元素为数组的最大值
    void BuildMaxHeap(int array[],int length )
    {
        heapSize = length;
        for(int i=length-1 ; i>=0 ; i--)
        {
            MaxHeapify(array , i );
        }
    }
     
    //排序
    void HeapSort(int array[] , int length)
    {
        //初始化堆
        BuildMaxHeap(array,length);
        for(int i=length ; i>=1 ; i--)
        {
            swap(array , 0 , i);
            //堆顶元素array[0](即数组的最大值)被置换到数组的尾部array[i]
            heapSize--;//从堆中移除该元素
            MaxHeapify(array , 0);//重建堆
        }
    }
    int main()
    {
        int a[8] = {68,20,39,88,97,46,59,11};
        int i;
        HeapSort(a,8);
        for(i=0 ; i<8 ; i++)
        {
            cout << a[i] << " ";
        }
        cout <<endl;
        return 0;
    }

    这是经过改正的,终于解决了.....

     

  • 相关阅读:
    扶桑号战列舰【单调栈+线段树】
    c++ vector基本函数、排序、查找用法
    路【邻接矩阵存图找最短路】
    free【分层图最短路】
    Magic Line【坐标点排序方法】
    Stones【中石油个人赛第十七场I】
    Second Large Rectangle【单调栈】
    点坐标求三角形面积【向量差乘】
    Random Point in Triangle【随机数解决期望值问题】
    JavaScript——基础知识,开始我们的js编程之旅吧!
  • 原文地址:https://www.cnblogs.com/ITtangtang/p/2474809.html
Copyright © 2011-2022 走看看