数据结构(C语言版 严蔚敏著)——内部排序

冒泡排序

基本思想:两两相邻记录的关键字 ,如果反序则交换,直到没有反序记录为止。

void BubbleSort(int k[], int n) {
    //冒泡排序是从下往上冒泡,相邻两个比较
    int temp, flag;
    flag = 1;//这里标志,  是对排序的优化
    for (int i = 0; i < n - 1 && flag; ++i)
        for (int j = n - 1; j > i; j--) {
            flag = 0;
            //如果到最上面两个比较的时候不进入下面的交换,冒泡可以提前结束
            if (k[j - 1] > k[j]) {
                temp = k[j - 1];
                k[j - 1] = k[j];
                k[j] = temp;
                flag = 1;
            }
        }
}


选择排序

通过n-i次关键字比较,从n-i+1个记录中选出关键字最小的记录,

并和第i(i<=i<=n)个记录交换。

void SelectSort(int k[],int n){
    int temp,min;

    for (int i = 0; i < n; ++i) {
        min=i;//min起始下标为i
        for (int j = i+1; j < n; ++j)
            if(k[j]<k[min])
                min=j;//找到最小元素的下标
        //交换
        temp=k[min];
        k[min]=k[i];
        k[i]=temp;
    }
}


直接插入排序

· 操作是将一个记录插入到已经排好序的有序表中,

  从而得到一个新的、记录增加1的有序表。

适合于 待排序数组基本有序,且记录较少。

void InsertSort(int k[], int n) {
    int temp, j;
    for (int i = 1; i < n; i++) {
        if (k[i] < k[i - 1]) {
            temp = k[i];
            for (j = i - 1; k[j] > temp&&j>=0; --j) {
                k[j + 1] = k[j];
            }
            k[j + 1] = temp;
        }
    }
}


希尔排序

是对直接插入排序的改进。O(n*logn)

void ShellInsert(int k[], int n) {
    int temp, j;
    int gap=n;
    do{             
        gap=gap/3+1;
        for (int i = gap; i < n; i++) {
                temp = k[i];
                for (j = i - gap; k[j] > temp&&j>=0; j-=gap) {
                    k[j + gap] = k[j];
                }
                //上面一步已经j-gap过了
                k[j + gap] = temp;
        }
    }while (gap>1);
}

堆排序

是对选择排序的改进。O(n*logn)

· 是一棵完全二叉树。

· 根结点一定是堆中所有结点最大者或最小者,如果按照层序遍历的方式给结点从

  1开始编号,则结点之间满足如下关系:


· 利用堆进行的排序算法,它的基本思想是:

    -将待排序的序列构成一个大顶堆(或小顶堆)

    -此时,整个序列的最大值就是堆顶的根结点。将它移走(就是将其与堆数组的末尾

      元素交换,此时末尾元素就是最大值)。

    -然后将剩余n-1个序列重新构成一个堆,这样就会得到n个元素中的最大值。

    -如此反复执行,便能得到一个有序序列了。

void swap(int k[], int i, int j) {
    int temp;
    temp = k[i];
    k[i] = k[j];
    k[j] = temp;
}

void HeapAdjust(int k[], int s, int n) {
    //k表示数组,s表示双亲结点,n表示元素个数
    int i, temp;
    temp = k[s];//存放需要调整的双亲结点
    //i = 2*s 为s的左孩子
    for (i = 2 * s; i <= n; i *= 2){
        //左孩子<右孩子,且i不超过n(待排长度)
        if (k[i] < k[i + 1] && i < n)
            i++;
        //如果双亲结点大,提前结束循环
        if(temp>=k[i])
            break;
        //两个孩子结点较大的到双亲结点
        k[s]=k[i];
        //交换的孩子结点的下标给s
        s=i;
    }
    k[s]=temp;
}

void HeapSort(int k[], int n) {
    //为了更好体现树的概念,k[0]不参与排序
    int i;
    //从整个堆最下面开始,构建大顶堆
    for (i = n / 2; i > 0; --i)
        HeapAdjust(k, i, n);
    for (i = n; i > 1; --i) {
        swap(k, 1, i);//堆顶元素与 i交换(最后一个元素)
        HeapAdjust(k, 1, i - 1);
    }
}


归并排序

· 假设初始序列有n个记录,则可以看成n个有序子序列,每个子序列长度为1,

  然后两两归并,得到[n/2]个长度为2或1的有序子序列;再两两归并,...,如此重复

  直到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。


#define MAXSIZE 10

//实现归并,并把最后结果放到list1,list1是待排序数组的头指针
void Merging(int *list1, int list1_size, int *list2, int list2_size) {
    int i, j, k, m;
    int temp[MAXSIZE];
    i = j = k = 0;
    while (i < list1_size && j < list2_size) {
        //比较大小,放入临时数组中
        if (list1[i] < list2[j])
            temp[k++] = list1[i++];
        else
            temp[k++] = list2[j++];
    }
    //把剩下的都放入临时数组中
    while (i < list1_size) {
        temp[k++] = list1[i++];
    }
    while (j < list2_size) {
        temp[k++] = list2[j++];
    }
    //临时数组覆盖 list1
    for (m = 0; m < list1_size + list2_size; m++)
        list1[m] = temp[m];
}

void MergeSort(int k[], int n) {
    //归并排序的递归算法
    if (n > 1) {
        int *list1 = k;//左半部分头指针
        int list1_size = n / 2;//左半部分大小
        int *list2 = k + n / 2;//右半部分头指针
        int list2_size = n - list1_size;//右半部分大小
        //递归拆分
        MergeSort(list1, list1_size);
        MergeSort(list2, list2_size);
        //逐一合并
        Merging(list1, list1_size, list2, list2_size);
    }
}


快速排序

· 是对冒泡排序的一种改进。基本思想是通过一趟排序将待排记录分割成独立的两部分,

  其中一部分记录的关键字都比另一部分的关键字小,则可分别对这两部分记录继续进行

  排序,以达到整个序列有序。

int Partition(int k[], int low, int high) {
    int point;
    //优化方案,选取三个元素比较大小,把中间的放到k[low]位置
//    int m=(low+high)/2;
//    if(k[low]>k[high])
//        swap(k,low,high);
//    if(k[m]>k[high])
//        swap(k,m,high);
//    if(k[m]>k[low])
//        swap(k,m,low);
    point = k[low];
    while (low < high) {
        while (low < high && k[high] >= point) {
            //比point大的都放后面,否则互换位置
            high--;
        }
        if(low==high)
            break;//提前结束
        swap(k, low, high);//将元素调换
        while (low < high && k[low] <= point) {
            //比point小的都放前面,否则互换位置
            low++;
        }
        swap(k, low, high);//将元素调换
    }//找到point在数组中的位置
    return low;//返回时,low = high
}

void QSort(int k[], int low, int high) {
    int point;
    if (low < high) {
        point = Partition(k, low, high);
        //对左边部分递归调用
        QSort(k, low, point - 1);
        //对右边部分递归调用
        QSort(k, point + 1, high);
    }
}

void QuickSort(int k[], int n) {
    QSort(k, 0, n - 1);//初始位置,最后的位置
}

排序总结

如何判断一个排序稳定与否,举个栗子:

排序前:5,6(1),1,4,3,6(2),(第一个6在第二个6之前)
排序后:如果排序后的结果是1,2,3,4,5,6(1),6(2)那么就说此排序算法是稳定的,即是稳定的排序。

             如果排序后的结果是1,2,3,4,5,6(2),6(1),即6(1)和6(2)相比较排序前,即是不稳定排序。





猜你喜欢

转载自blog.csdn.net/super_sloppy/article/details/79843878