选择排序(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;
}

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

 

原文地址:https://www.cnblogs.com/ITtangtang/p/2474809.html