1. 程式人生 > >資料結構和算法系列6 七大排序之直接選擇排序和堆排序

資料結構和算法系列6 七大排序之直接選擇排序和堆排序

上一篇我們總結了交換排序的氣泡排序和快速排序。那麼這一篇我們要總結的是選擇排序,選擇排序分為直接選擇排序和堆排序,我們主要分以下幾點進行總結。

1,直接選擇排序及演算法實現

2,堆排序及演算法實現

1,直接選擇排序及演算法實現

直接選擇排序(Straight Select Sort)是一種簡單的排序方法,它的基本思想是:通過n-i次關鍵字之間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,並和第i(1<=i<=n)個記錄交換位置。

比如,下圖展示了直接選擇排序的過程。

下面是演算法的實現程式碼。

C#版:

複製程式碼
namespace SelectionSort.CSharp
{
    
class Program { static void Main(string[] args) { List<int> list = new List<int> { 50, 10, 90, 30, 70, 40, 80, 60, 20 }; Console.WriteLine("********************直接選擇排序********************"); Console.WriteLine("排序前:"); Display(list); Console.WriteLine(
"排序後:"); SelectionSort(list); Display(list); Console.ReadKey(); } /// <summary> /// 直接選擇排序演算法 /// </summary> /// <returns>排序後的list</returns> public static void SelectionSort(List<int> list) {
//要遍歷的次數(遍歷n-1次) for (int i = 0; i < list.Count-1 ; i++) { //將當前下標定義為最小值下標 int min = i; //遍歷之後的資料 for (int j = i + 1; j <= list.Count-1; j++) { //如果有小於當前最小值的關鍵字,將它的下標賦值給min if (list[min] > list[j]) min = j; } //若min不等於i,說明找到真正最小值,交換真正最小值與之前假設最小值的位置 if (i != min) Swap(list,i,min); } } private static void Swap(List<int> list, int i, int min) { int temp=list[i]; list[i]=list[min]; list[min] = temp; } /// <summary> /// 列印列表元素 /// </summary> /// <param name="list"></param> private static void Display(List<int> list) { Console.WriteLine("\n**********展示結果**********\n"); if (list != null && list.Count > 0) { foreach (var item in list) { Console.Write("{0} ", item); } } Console.WriteLine("\n**********展示完畢**********\n"); } } }
複製程式碼

程式執行結果:

ds24

C語言版:

複製程式碼
/*包含標頭檔案*/
#include "stdio.h"
#include "stdlib.h"   
#include "io.h"
#include "math.h" 
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100

typedef int Status; 
typedef struct
{
    int data[MAXSIZE];
    int length;    
}SeqList;

/*交換
*注意要先宣告方法,然後再呼叫,否則會報錯
*/
void Swap(SeqList *seqList,int i,int min)
{
    int temp;
    temp=seqList->data[i];
    seqList->data[i]=seqList->data[min];
    seqList->data[min]=temp;
}

/*直接選擇排序*/
void SelectionSort(SeqList *seqList)
{
    int i,j,min;

    //要遍歷的次數(遍歷n-1次)
    for (i=0;i<seqList->length-1;i++)
    {
        //將當前下標定義為最小值下標
        min=i;

        //遍歷之後的資料
        for (j=i+1;j<=seqList->length-1;j++)
        {
            //如果有小於當前最小值的關鍵字,將它的下標賦值給min
            if (seqList->data[min]>seqList->data[j]) min=j;
        }

        //若min不等於i,說明找到真正最小值,交換真正最小值與之前假設最小值的位置
        if (i != min) Swap(seqList,i,min);
    }
}

/*列印結果*/
void Display(SeqList *seqList)
{
    int i;
    printf("\n**********展示結果**********\n");

    for (i=0;i<seqList->length;i++)
    {
        printf("%d ",seqList->data[i]);
    }

    printf("\n**********展示完畢**********\n");
}

#define N 9
void main()
{
    int i,j;
    SeqList seqList;

    //定義陣列和初始化SeqList
    int d[N]={50,10,90,30,70,40,80,60,20};

    for (i=0;i<N;i++)
    {
        seqList.data[i]=d[i];
    }
    seqList.length=N;

    printf("***************直接選擇排序***************\n");
    printf("排序前:");
    Display(&seqList);

    SelectionSort(&seqList);
    printf("\n排序後:");
    Display(&seqList);

    getchar();
}
複製程式碼

執行結果同C#版

2,堆排序及演算法實現

堆排序(Heap Sort) 利用堆(一般為大根堆)進行排序的方法。它的基本思想是:將待排序的序列構造成一個大根堆。此時,整個序列的最大值就是堆頂的根結點。將它移走(其實就是將其與堆陣列的末尾元素進行交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成一個大根堆,這樣就會得到n個元素中的次大值。如此反覆執行,便能得到一個有序序列了。

堆是具有下列性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱為大根堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱為小根堆,如下圖所示。

下面是其實現程式碼。

C#版:

複製程式碼
namespace HeapSort.CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> list = new List<int> { 50, 10, 90, 30, 70, 40, 80, 60, 20 };
            Console.WriteLine("********************堆排序********************");
            Console.WriteLine("排序前:");
            Display(list);

            Console.WriteLine("排序後:");
            HeapSort(list);
            Display(list);

            Console.ReadKey();
        }

        /// <summary>
        /// 堆排序演算法
        /// </summary>
        /// <param name="list"></param>
        public static void HeapSort(List<int> list)
        {
            //將無序堆構造成一個大根堆,大根堆有list.Count/2個父結點
            for (int i = list.Count / 2 - 1; i >= 0;i-- )
            {
                HeadAdjust(list,i,list.Count);
            }

            //逐步將每個最大值的根結點與末尾元素交換,並且再調整其為大根堆
            for (int i = list.Count - 1; i > 0; i--)
            {
                //將堆頂記錄和當前未經排序子序列的最後一個記錄交換位置
                Swap(list,0,i);
                HeadAdjust(list,0,i);
            }
        }

        /// <summary>
        /// 構造大根堆
        /// </summary>
        /// <param name="list"></param>
        /// <param name="parent"></param>
        /// <param name="length"></param>
        public static void HeadAdjust(List<int> list, int parent, int length)
        { 
            //儲存當前父結點
            int temp=list[parent];

            //得到左孩子結點
            int leftChild = 2 * parent + 1;

            while (leftChild < length)
            {
                //如果parent有右孩子,則要判斷左孩子是否小於右孩子
                if (leftChild + 1 < length && list[leftChild] < list[leftChild + 1])
                    leftChild++;

                //父親節點大於子節點,就不用做交換
                if (temp >= list[leftChild])
                    break;

                //將較大子節點的值賦給父親節點
                list[parent] = list[leftChild];

                //然後將子節點做為父親節點,已防止是否破壞根堆時重新構造
                parent = leftChild;

                //找到該父親節點較小的左孩子節點
                leftChild = 2 * parent + 1;
            }
            //最後將temp值賦給較大的子節點,以形成兩值交換
            list[parent] = temp;
        }

        #region Private Method

        private static void Swap(List<int> list, int top, int last)
        {
            int temp = list[top];
            list[top] = list[last];
            list[last] = temp;
        }

        private static void Display(List<int> list)
        {
            Console.WriteLine("\n**********展示結果**********\n");

            if (list != null && list.Count > 0)
            {
                foreach (var item in list)
                {
                    Console.Write("{0} ", item);
                }
            }

            Console.WriteLine("\n**********展示完畢**********\n");
        }

        #endregion
    }
}
複製程式碼

程式執行結果:

ds26

C語言版:

複製程式碼
/*包含標頭檔案*/
#include "stdio.h"
#include "stdlib.h"   
#include "io.h"
#include "math.h" 
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100

typedef int Status; 
typedef struct
{
    int data[MAXSIZE];
    int length;    
}SeqList;

/*構造大根堆*/
void HeapAdjust(SeqList *seqList,int parent,int length)
{
    int temp,leftChild;

    temp=seqList->data[parent];
    leftChild = 2 * parent + 1;

    while (leftChild < length)
    {
        if (leftChild + 1 < length && seqList->data[leftChild]<seqList->data[leftChild+1]) 
            leftChild++;

        if (temp >= seqList->data[leftChild])
            break;

        seqList->data[parent]=seqList->data[leftChild];

        parent = leftChild;

        leftChild = 2 * parent + 1;
    }
    seqList->data[parent] = temp;
}

/*交換元素*/
void Swap(SeqList *seqList,int top,int last)
{
    int temp=seqList->data[top];
    seqList->data[top]=seqList->data[last];
    seqList->data[last]=temp;
}

/*堆排序演算法*/
void HeapSort(SeqList *seqList)
{
    int i;
    for (i=seqList->length/2-1;i>=0;i--)
    {
        HeapAdjust(seqList,i,seqList->length);
    }

    for (i=seqList->length-1;i>0;i--)
    {
        Swap(seqList,0,i);
        HeapAdjust(seqList,0,i);
    }
}

/*列印結果*/
void Display(SeqList *seqList)
{
    int i;
    printf("\n**********展示結果**********\n");

    for (i=0;i<seqList->length;i++)
    {
        printf("%d ",seqList->data[i]);
    }

    printf("\n**********展示完畢**********\n");
}

#define N 9
void main()
{
    int i,j;
    SeqList seqList;

    //定義陣列和初始化SeqList
    int d[N]={50,10,90,30,70,40,80,60,20};

    for (i=0;i<N;i++)
    {
        seqList.data[i]=d[i];
    }
    seqList.length=N;

    printf("***************堆排序***************\n");
    printf("排序前:");
    Display(&seqList);

    HeapSort(&seqList);
    printf("\n排序後:");
    Display(&seqList);

    getchar();
}
複製程式碼

程式執行結果同上