数据结构与算法 7种排序算法的实现

1.冒泡排序

声明:本文章定义及代码搜集自 百度百科

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序算法的原理如下
在这里插入图片描述
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

#include <stdio.h>
 
#define ARR_LEN 255 /*数组长度上限*/
#define elemType int /*元素类型*/
 
/* 冒泡排序 */
/* 1. 从当前元素起,向后依次比较每一对相邻元素,若逆序则交换 */
/* 2. 对所有元素均重复以上步骤,直至最后一个元素 */
/* elemType arr[]: 排序目标数组; int len: 元素个数 */
void bubbleSort (elemType arr[], int len) {
    elemType temp;
    int i, j;
    for (i=0; i<len-1; i++) /* 外循环为排序趟数,len个数进行len-1趟 */
        for (j=0; j<len-1-i; j++) { /* 内循环为每趟比较的次数,第i趟比较len-i次 */
            if (arr[j] > arr[j+1]) { /* 相邻元素比较,若逆序则交换(升序为左大于右,降序反之) */
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
}
 
int main (void) {
    elemType arr[ARR_LEN] = {3,5,1,-7,4,9,-6,8,10,4};
    int len = 10;
    int i;
     
    bubbleSort (arr, len);
    for (i=0; i<len; i++)
        printf ("%d	", arr[i]);
    putchar ('
');
     
    return 0;
}

2.直接插入排序

直接插入排序(Straight Insertion Sort)是一种最简单的排序方法,其基本操作是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表。

#include<iostream>
using namespace std;
int main()
{
    int a[]={98,76,109,34,67,190,80,12,14,89,1};
    int k=sizeof(a)/sizeof(a[0]);
    int i,j;
    for(i=1;i<k;i++)//循环从第2个元素开始
    {
        if(a[i]<a[i-1])
        {
            int temp=a[i];
            for(j=i-1;j>=0 && a[j]>temp;j--)
            {
                a[j+1]=a[j];
            }
            a[j+1]=temp;//此处就是a[j+1]=temp;
        }
    }
    for(int f=0;f<k;f++)
    {
        cout<<a[f]<<"  ";
    }
    return 0;
}

python版

def insertSort(relist):
    len_ = relist.__len__()
    for i in range(1,len_):
        for j in range(i):
            if relist[i] < relist[j]:
                relist.insert(j,relist[i])  # 首先碰到第一个比自己大的数字,赶紧刹车,停在那,所以选择insert
                relist.pop(i+1)  # 因为前面的insert操作,所以后面位数+1,这个位置的数已经insert到前面去了,所以pop弹出
                break
    return relist

print(insertSort( arr ))

3.简单选择排序

简单选择排序是指一种排序算法,在简单选择排序过程中,所需移动记录的次数比较少。最好情况下,即待排序记录初始状态就已经是正序排列了,则不需要移动记录。
方法是设所排序序列的记录个数为n。i取1,2,…,n-1,从所有n-i+1个记录(Ri,Ri+1,…,Rn)中找出排序码最小的记录,与第i个记录交换。执行n-1趟 后就完成了记录序列的排序。

void Select_Sort(datatype R[],int n)
{   //对排序表R[1].....R[n]进行冒泡排法,n是记录个数
    for(i=1; i<n; i++)  /*做n-1趟选取*/
    {
        k=i;    /*在i开始的n-i+1个记录中选关键码最小的记录*/
        for(j=i+1;j<=n;j++)
            if(R[j].key<R[k].key)
                k=j;    /*k中存放关键码最小记录的下标*/
        if(i!=k)    /*关键码最小的记录与第i个记录交换*/
        {
            int temp;
            temp=R[k];
            R[k]=R[i];
            R[i]=temp;
        }
    }
}

java版

public class SimpleSort{
    public static void sort(Comparable[] data){
        //数组长度
        int len=data.length;
        for(int i=0; i<len; i++){
            //记录当前位置
            int position = i;
            //找出最小的数,并用position指向最小数的位置
            for(int j=i+1; j<len; j++){
                if( data[position].compareTo(data[j]) > 0 ) {
                    position=j;
                }//endif
            }//endfor
            //交换最小数data[position]和第i位数的位置
            Comparable temp=data[i];
            data[i]=data[position];
            data[position]=temp;
        }//endfor
    }//endsort
    public static void main(String[] args) {
        //在JDK1.5版本以上,基本数据类型可以自动装箱
        //int,double等基本类型的包装类已实现了Comparable接口
        Comparable[] c={4,9,23,1,45,27,5,2};
        sort(c);
        for(Comparable data:c) {
            System.out.println(data);
        }
    }
}

4,希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
[外链图片转存失败(img-gAPtsfdt-1562315491107)(https://baike.baidu.com/pic/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F/3229428/0/9f510fb30f2442a77555f25dd343ad4bd01302ea?fr=lemma&ct=single#aid=0&pic=9f510fb30f2442a77555f25dd343ad4bd01302ea)]

public static void main(String [] args)
{
    int[]a={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<a.length;i++)
        {
            System.out.print(a[i]+" ");
        }
        //希尔排序
        int d=a.length;
            while(true)
            {
                d=d/2;
                for(int x=0;x<d;x++)
                {
                    for(int i=x+d;i<a.length;i=i+d)
                    {
                        int temp=a[i];
                        int j;
                        for(j=i-d;j>=0&&a[j]>temp;j=j-d)
                        {
                            a[j+d]=a[j];
                        }
                        a[j+d]=temp;
                    }
                }
                if(d==1)
                {
                    break;
                }
            }
            System.out.println();
            System.out.println("排序之后:");
                for(int i=0;i<a.length;i++)
                {
                    System.out.print(a[i]+" ");
                }
    }

5.堆排序

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(Build Max Heap):将堆中的所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

def big_endian(arr, start, end):
   root = start
   while True:
       child = root * 2 + 1   # 左孩子
       if child > end:        # 孩子比最后一个节点还大 也就意味着最后一个叶子节点了 就得跳出去一次循环已经调整完毕
           break
       if child + 1 <= end and arr[child] < arr[child + 1]:   # 为了始终让其跟子元素的较大值比较 如果右边大就左换右,左边大的话就默认
           child += 1
       if arr[root] < arr[child]:     # 父节点小于子节点直接换位置 同时坐标也得换这样下次循环可以准确判断是否为最底层是不是调整完毕
           arr[root], arr[child] = arr[child], arr[root]
           root = child
       else:                            # 父子节点顺序正常 直接过
           break
            
            
def heap_sort(arr):
   # 无序区大根堆排序
   first = len(arr) // 2 - 1
   for start in range(first, -1, -1):   # 从下到上,从右到左对每个节点进调整 循环得到非叶子节点
       big_endian(arr, start, len(arr) - 1)  # 去调整所有的节点
   for end in range(len(arr) - 1, 0, -1):
       arr[0], arr[end] = arr[end], arr[0]   # 顶部尾部互换位置
       big_endian(arr, 0, end - 1)          # 重新调整子节点的顺序  从顶开始调整
   return arr
    
    
def main():
   l = [3, 1, 4, 9, 6, 7, 5, 8, 2, 10]
   print(heap_sort(l))  # 原地排序
    
if __name__ == "__main__":
   main() 

6.归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;

[外链图片转存失败(img-YKQQAprf-1562316056707)(https://baike.baidu.com/pic/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/1639015/0/c8177f3e6709c93d673b9ed49d3df8dcd00054c3?fr=lemma&ct=single#aid=0&pic=c8177f3e6709c93d673b9ed49d3df8dcd00054c3)]

def MergeSort(lists):
    if len(lists) <= 1:
        return lists
    num = int( len(lists) / 2 )
    left = MergeSort(lists[:num])
    right = MergeSort(lists[num:])
    return Merge(left, right)
def Merge(left,right):
    r, l=0, 0
    result=[]
    while l<len(left) and r<len(right):
        if left[l] <= right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += list(left[l:])
    result += list(right[r:])
    return result
print MergeSort([1, 2, 3, 4, 5, 6, 7, 90, 21, 23, 45])

7.快速排序算法

快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
示例
假设用户输入了如下数组:
下标
0,1,2,3,4 ,5
数据
【6,2,7,3,8,9】
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
我们要把所有比k小的数移动到k的左面,所以我们可以开始寻找比6小的数,从j开始,从右往左找,不断递减变量j的值,我们找到第一个下标3的数据比6小,于是把数据3移到下标0的位置,把下标0的数据6移到下标3,完成第一次比较:
下标
0,1,2,3,4,5
数据
【3,2,7,6,8,9】
i=0 j=3 k=6
接着,开始第二次比较,这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2的数据是第一个比k大的,于是用下标2的数据7和j指向的下标3的数据的6做交换,数据状态变成下表:
下标
0,1,2,3,4,5
数据
【3,2,6,7,8,9】
i=2 j=3 k=6
称上面两次比较为一个循环。
接着,再递减变量j,不断重复进行上面的循环比较。
在本例中,我们进行一次循环,就发现i和j“碰头”了:他们都指向了下标2。于是,第一遍比较结束。得到结果如下,凡是k(=6)左边的数都比它小,凡是k右边的数都比它大:
下标
0,1,2,3,4,5
数据
【3,2,6,7,8,9】
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
然后,对k两边的数据,再分组分别进行上述的过程,直到不能再分组为止。
注意:第一遍快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
调用函数
在C语言中可以用函数qsort()可以直接为数组进行排序。 [1]
用 法:
void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));
参数:
  1 待排序数组首地址
  2 数组中待排序元素数量
  3 各元素的占用空间大小
  4 指向函数的指针,用于确定排序的顺序

def quick_sort(data):    
    """快速排序"""    
    if len(data) >= 2:  # 递归入口及出口        
        mid = data[len(data)//2]  # 选取基准值,也可以选取第一个或最后一个元素        
        left, right = [], []  # 定义基准值左右两侧的列表        
        data.remove(mid)  # 从原始数组中移除基准值        
        for num in data:            
            if num >= mid:                
                right.append(num)            
            else:                
                left.append(num)        
        return quick_sort(left) + [mid] + quick_sort(right)    
    else:        
        return data
 
# 示例:
array = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12]
print(quickSort(array))
# 输出为[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]
原文地址:https://www.cnblogs.com/BIG-BOSS-ZC/p/11807361.html