排序


排序是指将集合中的元素按照某种顺序排列的过程。

插入排序

插入排序,一般称为直接插入排序。直接插入排序的基本思想是顺序地把待排序的数据元素按其值的大小插入到已排序数据元素子集合的适当位置。插入排序的时间复杂度为 O(n2),空间复杂度为 O(1)

Python 实现插入排序:

def insertionSort(alist):
    for index in range(1, len(alist)):
        currentvalue = alist[index]
        position = index

        while position > 0 and alist[position-1] > currentvalue:
            alist[position] = alist[position-1]
            position = position - 1

        alist[position] = currentvalue
        
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
insertionSort(alist)
print(alist)

执行结果:

[17, 20, 26, 31, 44, 54, 55, 77, 93]

C 实现插入排序:

#include <stdio.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

void insertSort(DType list[], int n)
{
    int i, j;
    DType temp;
    for (int i = 1; i < n; i++)
    {
        temp = list[i];
        j = i;

        while (j > 0 && temp.key < list[j-1].key)
        {
            list[j] = list[j-1];
            j--;
        }
        list[j] = temp;
    }
    
}

int main(int argc, char argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int i = 0;
    int n = 9;

    insertSort(alist, n);

    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");
    
    return 0;
}

执行结果:

17 20 26 31 44 54 55 77 93 

希尔排序

希尔排序也称递减增量排序(缩小增量排序),它对插入排序做了改进。希尔排序的基本思想是把待排序的数据元素分成若干小组,对同一小组内的数据元素用直接插入法排序;小组的个数逐次减少;当完成了所有数组元素都在一个组内的排序后,排序过程结束。小组并不是连续切分的,而是使用增量 i(有时称作步长)选取所有间隔为 i 的元素组成子小组。

Python 实现希尔排序:

def gapInsertionSort(alist, start, gap):
    for i in range(start+gap, len(alist), gap):
        currentvalue = alist[i]
        position = i

        while position >= gap and alist[position-gap] > currentvalue:
            alist[position] = alist[position-gap]
            position = position -  gap

        alist[position] = currentvalue

def shellSort(alist):
    sublistcount = len(alist) // 2
    while sublistcount > 0:
        for startposition in range(sublistcount):
            gapInsertionSort(alist, startposition, sublistcount)
            print("After increments of size ", sublistcount, "(", startposition, ")"," The list is ", alist)

        sublistcount = sublistcount // 2
        
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print(alist)
shellSort(alist)

执行结果:

[54, 26, 93, 17, 77, 31, 44, 55, 20]
After increments of size  4 ( 0 )  The list is  [20, 26, 93, 17, 54, 31, 44, 55, 77]
After increments of size  4 ( 1 )  The list is  [20, 26, 93, 17, 54, 31, 44, 55, 77]
After increments of size  4 ( 2 )  The list is  [20, 26, 44, 17, 54, 31, 93, 55, 77]
After increments of size  4 ( 3 )  The list is  [20, 26, 44, 17, 54, 31, 93, 55, 77]
After increments of size  2 ( 0 )  The list is  [20, 26, 44, 17, 54, 31, 77, 55, 93]
After increments of size  2 ( 1 )  The list is  [20, 17, 44, 26, 54, 31, 77, 55, 93]
After increments of size  1 ( 0 )  The list is  [17, 20, 26, 31, 44, 54, 55, 77, 93]

上面的 Python 示例,先为 n/2 个子列表排序,接着为 n/4 个子列表排序。最终,整个列表由基本的插入排序散发排好序。希尔排序的时间复杂度介于 O(n) 和 O(n2) 之间,通过改变增量,比如采用,希尔排序的时间复杂度可以达到 ***O(n3/2)***。

C 语言实现希尔排序:

#include <stdio.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

/**
 * 希尔排序
 * list     [IN]待排序数组          [OUT]完成排序后的数组 
 * n        [IN]待排序数组长度
 * d        [IN]希尔增量值数组
 * numOfD   [IN]希尔增量值数组长度
 */
void shellSort(DType list[], int n, int d[], int numOfD)
{
    int i, j, k, m, span;
    DType temp;

    for (m = 0; m < numOfD; m++)
    {
        span = d[m];
        for (k = 0; k < span; k++)
        {
            for ( i = k; i < n-span; i = i+span)
            {
                temp = list[i+span];
                j = i;
                while (j > -1 && temp.key <= list[j].key)
                {
                    list[j+span] = list[j];
                    j = j - span;
                }
                list[j+span] = temp;
            }
        }
    }
    
}

int main(int argc, char argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int d[3] = {6, 3, 1};
    int i = 0;
    int n = 9;
    int numOfD = 3;

    shellSort(alist, n, d, numOfD);

    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");

    return 0;
}

执行结果:

17 20 26 31 44 54 55 77 93

希尔算法的空间复杂度为 ***O(1)***。

冒泡排序

利用交换数据元素的位置进行排序的方法称为交换排序。常用的交换排序方法有冒泡排序法和快速排序法(快速排序法是一种分区交换排序方法)。

冒泡排序需要多次遍历列表,它比较相邻的元素,将不合顺序的交换。每一轮遍历都将下一个最大值放到正确的位置上。如果列表有 n 个元素,那么第一轮遍历要比较 n-1 对。第二轮遍历开始时,最大值已经在正确位置上,还剩 n-1 个元素需要排列,也就是要比较 n-2 对。依次类推,给含有 n 个元素的列表排序需要遍历 n-1 论,总的比较次数是前 n-1 个整数之和,即:
f ( n ) = 1 + 2 + 3 + . . . + ( n − 1 ) = ( n − 1 ) 2 + ( n − 1 ) 2 = n 2 − n 2 f(n) = 1 + 2 + 3 + ... + (n-1) = \frac{(n-1)^2+(n-1)}{2} = \frac{n^2-n}{2} f(n)=1+2+3+...+(n1)=2(n1)2+(n1)=2n2n
所以,冒泡算法的时间复杂度是 ***O(n2)***。

Python 实现冒泡算法:

def bubbleSort(alist):
    for passnum in range(len(alist)-1, 0, -1):
        for i in range(passnum):
            if alist[i] > alist[i+1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp

短冒泡

冒泡排序要遍历列表中未排序的部分,但如果在一轮遍历中没有发生元素交换,证明序列已经有序,可以提前终止排序,这种排序被称作短冒泡

Python 实现短冒泡算法:

def shortBubbleSort(alist):
    exchanges = True
    passnum = len(alist) - 1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i] > alist[i+1]:
                exchanges = True
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp
        passnum = passnum - 1

C 语言实现冒泡算法:

#include <stdio.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

void bubbleSort(DType list[], int n)
{
    int i, j, flag = 1;
    DType temp;
    for (i = 0; i < n && flag == 1; i++)
    {
        flag = 0;
        for (j = 0; j < n-1; j++)
        {
            if (list[j].key > list[j+1].key)
            {
                flag = 1;
                temp = list[j];
                list[j] = list[j+1];
                list[j+1] = temp;
            }
        }
    }
}

int main(int argc, char argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int i = 0;
    int n = 9;

    bubbleSort(alist, n);

    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");
    
    return 0;
}

执行结果:

17 20 26 31 44 54 55 77 93 

冒泡算法的空间复杂度为 ***O(1)***。

选择排序

选择排序在冒泡排序的基础上做了改进,每次遍历元素集合时只做一次交换。选择排序在每次遍历时寻找最大值(或最小值),并在遍历完成之后将它放到正确的位置上。和冒泡排序一样第一次遍历之后,最大(或最小)元素就位,依次类推。和冒泡排序类似,选择排序的时间复杂度也为 O(n2),空间复杂度为 O(1)

Python 实现选择排序:

def selectionSort(alist):
    for fillslot in range(len(alist)-1, 0, -1):
        positionOfMax = 0
        for location in range(1, fillslot+1):
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location

            temp = alist[fillslot]
            alist[fillslot] = alist[positionOfMax]
            alist[positionOfMax] = temp

C 实现选择排序:

#include <stdio.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

void selectionSort(DType alist[], int n)
{
    int i, j, small;
    DType temp;

    for (i = 0; i < n-1; i++)
    {
        small = i;
        for (j = 0; j < n; j++)
        {
            if (alist[j].key < alist[small].key)
            {
                small = j;
            }
            
            if (small != i)
            {
                temp = alist[i];
                alist[i] = alist[small];
                alist[small] = temp;
            }
        }
    }
}

int main(int argc, char *argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int i = 0;
    int n = 9;

    selectionSort(alist, n);

    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");

    return 0; 
}

执行结果:

93 77 55 54 44 31 26 17 20 

归并排序

归并排序每次将一个集合一分为二,如果子集合为空或只有一个元素,那么从定义上来说它就是有序的。如果集合不止一个元素,就将集合一分为二,并对两部分都递归调用归并排序。当两部分都有序后,就进行归并(将两个较小的有序列表归并为一个有序列表的过程)这一基础操作。

Python 实现归并排序:

def mergeSort(alist):
    print("Splitting ", alist)
    if len(alist) > 1:
        mid = len(alist) // 2
        lefthaff = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthaff)
        mergeSort(righthalf)

        i = 0
        j = 0
        k = 0
        while i < len(lefthaff) and j < len(righthalf):
            if lefthaff[i] < righthalf[j]:
                alist[k] = lefthaff[i]
                i = i + 1
            else:
                alist[k] = righthalf[j]
                j = j + 1
            k = k + 1
        
        while i < len(lefthaff):
            alist[k] = lefthaff[i]
            i = i + 1
            k = k + 1
        
        while j < len(righthalf):
            alist[k] = righthalf[j]
            j = j + 1
            k = k + 1
        
    print("Merging ", alist)

alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
mergeSort(alist)

执行结果:

Splitting  [54, 26, 93, 17, 77, 31, 44, 55, 20]
Splitting  [54, 26, 93, 17]
Splitting  [54, 26]
Splitting  [54]
Merging  [54]
Splitting  [26]
Merging  [26]
Merging  [26, 54]
Splitting  [93, 17]
Splitting  [93]
Merging  [93]
Splitting  [17]
Merging  [17]
Merging  [17, 93]
Merging  [17, 26, 54, 93]
Splitting  [77, 31, 44, 55, 20]
Splitting  [77, 31]
Splitting  [77]
Merging  [77]
Splitting  [31]
Merging  [31]
Merging  [31, 77]
Splitting  [44, 55, 20]
Splitting  [44]
Merging  [44]
Splitting  [55, 20]
Splitting  [55]
Merging  [55]
Splitting  [20]
Merging  [20]
Merging  [20, 55]
Merging  [20, 44, 55]
Merging  [20, 31, 44, 55, 77]
Merging  [17, 20, 26, 31, 44, 54, 55, 77, 93]

C 语言实现归并排序:

#include <stdio.h>
#include <stdlib.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

void merge(DType alist[], int n, DType swap[], int k)
{
    int m = 0;
    int u1, l2, i, j, u2;
    int l1 = 0;

    while (l1+k <= n-1)
    {
        l2 = l1 + k;
        u1 = l2 - 1;
        u2 = (l2+k-1 <= n-1)?l2+k-1:n-1;
        for (i = l1, j = l2; i <= u1 && j <= u2; m++)
        {
            if (alist[i].key <= alist[j].key)
            {
                swap[m] = alist[i];
                i++;
            }
            else
            {
                swap[m] = alist[j];
                j++;
            }
        }
        
        while (i <= u1)
        {
            swap[m] = alist[i];
            m++;
            i++;
        }
        
         while (j <= u2)
        {
            swap[m] = alist[j];
            m++;
            j++;
        }

        l1 = u2 + 1;
    }
    
    for (i = l1; i < n; i++, m++)
    {
        swap[m] = alist[i];
    }
}

void mergeSort(DType alist[], int n)
{
    int i;
    int k = 1;
    DType *swap;

    swap = (DType *)malloc(sizeof(DType)*n);
    while (k < n)
    {
        merge(alist, n, swap, k);
        for (i = 0; i < n; i++)
            alist[i] = swap[i];
        k = 2 * k;
    }
    free(swap);
}

int main(int argc, char argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int i = 0;
    int n = 9;
    
    mergeSort(alist, n);
    
    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");

    return 0;
}

执行结果:

17 20 26 31 44 54 55 77 93

归并算法的时间复杂度为 O(nlogn),空间复杂度为 O(n)

快速排序

快速排序也称为基数排序,其基本思想是:设元素集合 list 中存放了 n 个数据元素,low 为 list 的低端下标,height 为 list 的高端下标,从 list 中任取一个元素(通常为集合的第一个元素 list[low])作为基准值,然后调整 list 中各个元素的位置,使排在标准值前面元素的值均小于标准值,排在标准值后面元素的值均大于或等于标准元值(再最终的有序集合中,基准值的位置通常被称作分割点,算法在分割点切分集合,以进行对快速排序的子调用)。在这样一次过程结束后,以标准值元素为中心,分为两个子集合。然后再对这两部分递归调用快速排序。递归算法的结束条件是 height ⩽ low,即上界下标小于或等于下界下标。

Python 实现快速排序:

def partition(alist, first, last):
    pivotvalue = alist[first]

    leftmask = first + 1
    rightmask = last
    done = False

    while not done:
        while leftmask <= rightmask and alist[leftmask] <= pivotvalue:
            leftmask = leftmask + 1

        while alist[rightmask] >= pivotvalue and rightmask >= leftmask:
            rightmask = rightmask - 1

        if rightmask < leftmask:
            done = True
        else:
            temp = alist[leftmask]
            alist[leftmask] = alist[rightmask]
            alist[rightmask] = temp

    temp = alist[first]
    alist[first] = alist[rightmask]
    alist[rightmask] = temp

    return rightmask

def quickSortHelper(alist, first, last):
    if first < last:
        splitpoint = partition(alist, first, last)
        
        quickSortHelper(alist, first, splitpoint-1)
        quickSortHelper(alist, splitpoint+1, last)

def quickSort(alist):
    quickSortHelper(alist, 0, len(alist)-1)

Python 快速排序示例:

alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
quickSort(alist)
print(alist)

执行结果:

[17, 20, 26, 31, 44, 54, 55, 77, 93]

C 语言实现快速排序:

#include <stdio.h>
#include <stdlib.h>

typedef int KeyType;
typedef struct DataType
{
    KeyType key;
}DType;

void quickSort(DType alist[], int low, int high)
{
    int i = low, j = high;
    DType temp = alist[low];

    while (i < j)
    {
        while (i < j && temp.key <= alist[j].key)
        {
            j--;
        }

        if (i < j)
        {
            alist[i] = alist[j];
            i++;
        }
            
        while (i < j && alist[i].key < temp.key)
        {
            i++;
        }
                
        if (i < j)
        {
            alist[j] = alist[i];
            j--;
        }
    }
    alist[i] = temp;
    if (low < i)
        quickSort(alist, low, i-1);
    if (i < high)
        quickSort(alist, j+1, high);
}

int main(int argc, char argv[])
{
    DType alist[9] = {54, 26, 93, 17, 77, 31, 44, 55, 20};
    int i = 0;
    int n = 9;
    int low = 0;
    int high = 8;
    
    quickSort(alist, low, high);
    
    for (int i = 0; i < n; i++)
    {
        printf("%d ", alist[i].key);
    }
    printf("\n");

    return 0;
}

执行结果:

17 20 26 31 44 54 55 77 93 

最好的情况下,快速排序的时间复杂度为 O(nlogn),最坏的情况(分割点不在列表中部,而是偏向某一端)下时间复杂度为 O(n2)。最坏的情况下,快速排序的空间复杂度为 O(n)

为了避免最坏的情况,可以使用三数取中法避免切分不均匀,即在选择基准值时考虑集合的头元素、中间元素与尾元素,然后取它们的中间值。比如:alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]中先选取 54、77 和 20,然后取中间值 54。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二流人物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值