八大排序算法的实现和分析

1.插入排序—直接插入排序(Straight Insertion Sort)

算法思想:
每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从后向前扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。

稳定性分析:
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
这里写图片描述

代码实现_C/C++

#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 j;
    for(int 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;
}

效率:
时间复杂度:O(n^2)

2.插入排序—希尔排序(Shell`s Sort)

算法思想:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中,先在各组内进行直接插入排序;然后,取第二个增量d2<
d1重复上述的分组和排序,直至所取的增量d =1,即所有记录放在同一组中进行直接插入排序为止。

稳定性分析:
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
这里写图片描述

代码实现_java:

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){
            for(int i=0;i<d;i++){      //外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置
                for(int j=i;j+d<a.length;j+=d){
                int temp;
                if(a[j]>a[j+d]){
                    temp=a[j];
                    a[j]=a[j+d];
                    a[j+d]=temp;
                    }
                }
            }
            if(d==1){break;}
            d--;
           }

            System.out.println();
            System.out.println("排序之后:");
                for(int i=0;i<a.length;i++)
                {
                    System.out.print(a[i]+" ");
                }
    }

3. 选择排序—简单选择排序(Simple Selection Sort)

基本思想:

在要排序的一组数中,选出最小(最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推。知道第n-1个元素(倒数第二个数)和第n个数(最后一个数)比较为止。

这里写图片描述

简单排序处理流程

(1) 从待排序序列中,找到关键字最小的元素;

(2) 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;

(3) 从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。

稳定性分析:

举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。直接选择排序的平均时间复杂度为O(n^2)

代码实现:

  public static void main(String [] args) {
    int[]list={49,38,65,97,76,13,27,49,78,34,12,64,1};
    // 需要遍历获得最小值的次数
    // 要注意一点,当要排序 N 个数,已经经过 N-1 次遍历后,已经是有序数列
    for (int i = 0; i < list.length - 1; i++) {
        int temp = 0;
        int index = i; // 用来保存最小值得索引
        // 寻找第i个小的数值
        for (int j = i + 1; j < list.length; j++) {
            if (list[index] > list[j]) {
                index = j;
            }
        }
        // 将找到的第i个小的数值放在第i个位置上
        temp = list[index];
        list[index] = list[i];
        list[i] = temp;
        System.out.print(list[i]+" ");
    }
   }

与冒泡排序的区别:

1、选择排序 :a[0]与a[1] 比较,如果 a[0]大于a[1], 记录最小数a[1]的位置1,然后j++,变成了a[2]与当前最小的数a[1]比较,慢慢往后循环,记录最小的数,最好将最小的数与第一个数a[0]交换位置

2、冒泡排序:首先第一个数 a[0]与第二个数a[1] 比较(从小到大),然后第二个数 a[1]与第三个数a[2] 比较;第三个数 a[2]与第四个数a[3]…… 第一内层循环结束;

4. 选择排序—堆排序(Heap Sort)

            //详细见下一篇博客

5. 交换排序—冒泡排序(Bubble Sort)

算法思想:
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

过程:
这里写图片描述

代码实现_C++:

void bubbleSort(int a[], int n){  
    for(int i =0 ; i< n-1; ++i) {  
        for(int j = 0; j < n-i-1; ++j) {  
            if(a[j] > a[j+1])  
            {  
                int tmp = a[j] ; a[j] = a[j+1] ;  a[j+1] = tmp;  
            }  
        }  
    }  
}  

6. 交换排序—快速排序(Quick Sort)

基本思想:

通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可继续对这两部分记录分别进行排序,以达到整个序列有序。

方法步骤:

    在快速排序中首先要考虑的是如何选取序列中一个元素的关键字的值作为界值(该关键字设为T)。一般选取第一个元素、中间元素、终端元素的值。
    以选取序列中第一个元素的值为界值。(设为P(k),并将P(k)赋值给T)。快排使用两头向中间扫描的办法,同时交换与基准记录逆序的记录。
    (1)设置两个指针left和right,(分别为i和j),分别从表的两端向中间移动,将界值元素放入一个暂存单元。
    (2)将指针right对序列自右向左扫描,直到找到一个关键字小于界值的元素,将该元素移至left指针所指的位置。(j--,直到发现一个P(j)<T为止,将P(j)移动到P(i)的位置)。
    (3)将指针left对序列自左向右扫描,直到找到一个关键字大于或等于界值的元素,将该元素移至right指针所指的位置。(i++,直到发现一个P(i)>T为止,将P(i)移动到P(j)的位置)。
    (4)重复(2)(3)部,直到指针left和right相遇,此时,指针所在位置的左边元素关键字值均小于界值,右边所有元素关键字值均大于界值。将界值元素放入指针相遇的位置。(直到i=j为止,此时将T移动到P(i)的位置上)
    (5)对产生的两个子序列,再进行如上所述的划分,直到所有子序列中只有一个元素为止。排序过程结束。

一趟排序过程:

这里写图片描述

排序的全过程:

这里写图片描述

代码实现_C:

    void sortint *a, int left, int right){
        if (left>=right){/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
            return;
            }
        int i = left ;
        int j = right;
        int key = a[left];
        while(i<j){  /*控制在当组内寻找一遍*/
        while(i < j && key <= a[j]){//使循环找到一个a[j]<key为止
          j--; //right指针向前寻找
        }
        a[i] = a[j];   //将找到的a[j]移到a[i](left指针)的位置

        while(i<j&&key>=a[i]){ //使循环找到一个a[i]>key为止
        i++; //left指针向后寻找
      }
      a[j] = a[i]; //将找到的a[i]移到a[j](right指针)的位置
}
      a[i] = key; /*当在当组内找完一遍以后就把中间数key回归*/
    sort(a, left, i - 1);/*用同样的方式对分出来的左边的小组进行快排*/
    sort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行快排*/
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章