数据结构排序(简单易懂口语化)--冒泡,快速,直接插入,希尔,直接选择,堆,归并,基数

 

1、排序

1)冒泡排序

从数组中第一个数开始,一直跟后一个数比较,小的放前面,大的放后面。

稳定性:稳定      平均时间复杂度:O(n^2)

//array[]为待排序数组,n为数组长度
void BubbleSort(int array[], int n)
{
    int i, j, k;
    for(i=0; i<n-1; i++)
        for(j=0; j<n-1-i; j++)
        {
            if(array[j]>array[j+1])
            {
                k=array[j];
                array[j]=array[j+1];
                array[j+1]=k;
            }
        }
}

2)快速排序

①以第一个为基准,定一个变量j从后面往前数,找到第一个比它小的,交换位置,j--;

②另定一个变量i从前面往后数,找到第一个比它大的,交换位置,i++;

③如果找不到,就直接j--,i++;

④一直到i == j

稳定性:不稳定   平均时间复杂度:O(nlogn)

    /**
     * (int[] a,left,right)
     * 1、一般默认第一个数temp = a[left]是基准值,令i=left,j=right
     * 2、while循环一:i!=j,左边界不等于右边界
     * 3、循环二:从后往前找到第一个比基准值小的数,循环三:从前往后找到第一个比基准值大的数
     * 3、交换这两个数的位置
     * 4、循环一终止,此时i==j
     * 5、交换基准值和a[i],因为上面的循环没有对a[i]这个数字做处理
     * 6、递归执行左边界(a,left,i-1)
     * 7、递归执行右边界(a,i+1,right)
     */
    public void adjust_quicksort(int k[],int n)
    {
        quicksort(k,0,n-1);
    }
    void quicksort(int a[], int left, int right)
    {
        int i,j,t,temp;
        if(left>right)   //(递归过程先写结束条件)
            return;

        temp=a[left]; //temp中存的就是基准数
        i=left;
        j=right;
        while(i!=j)
        {
            //顺序很重要,要先从右边开始找(最后交换基准时换过去的数要保证比基准小,因为基准
            //选取数组第一个数,在小数堆中)
            while(a[j]>=temp && i<j)
                j--;
            //再找左边的
            while(a[i]<=temp && i<j)
                i++;
            //交换两个数在数组中的位置
            if(i<j)
            {
                t=a[i];
                a[i]=a[j];
                a[j]=t;
            }
        }
        //最终将基准数归位 (之前已经temp=a[left]过了,交换只需要再进行两步)
        //因为前面是比基准数小和比基准数大的数字交换,现在i==j了
        //这个i就是基准值的新下标,且a[i]比基准值小,所以交换这两个数字,完成一趟快排
        a[left]=a[i];
        a[i]=temp;

        quicksort(a,left,i-1);//继续处理左边的,这里是一个递归的过程
        quicksort(a,i+1,right);//继续处理右边的 ,这里是一个递归的过程
    }

3)直接插入排序

从第二个数开始依次跟前面的数比较,比前面的数小就放在那个数的前面,一直循环完。

稳定性: 稳定       平均时间复杂度:O(n^2)

public void insertSort(int[] array){
        for(int i=1;i<array.length;i++)//第0位独自作为有序数列,从第1位开始向后遍历
        {
            if(array[i]<array[i-1])//0~i-1位为有序,若第i位小于i-1位,继续寻位并插入,否则认为0~i位也是有序的,忽略此次循环,相当于continue
            {
                int temp=array[i];//保存第i位的值
                int j = i - 1;
                while(j>=0 && temp<array[j])//从第i-1位向前遍历并移位,直至找到小于第i位值停止
                {
                    array[j+1]=array[j];
                    j--;
                }
                array[j+1]=temp;//插入第i位的值
            }
        } 
    }

4)希尔排序

每次增量为n/2的数,分成n/2部分,然后排序,一直到n/2为1。

稳定性:不稳定    平均时间复杂度:O(n^2) 

è¿éåå¾çæè¿°

//下面是插入排序
void InsertSort( int array[], int n)
{
    int i,j,temp;
    for( i=0;i<n;i++ )
    {
        if(array[i]<array[i-1])
        {
            temp=array[i];
            for( j=i-1;array[j]>temp;j--)
            {
                array[j+1]=array[j];
            }
            array[j+1]=temp;
        }
    }
}
//在插入排序基础上修改得到希尔排序
void SheelSort( int array[], int n)
{
    int i,j,temp;
    int gap=n; //~~~~~~~~~~~~~~~~~~~~~
    do{
        gap=gap/3+1;  //~~~~~~~~~~~~~~~~~~
        for( i=gap;i<n;i++ )
        {
            if(array[i]<array[i-gap])
            {
                temp=array[i];
                for( j=i-gap;array[j]>temp;j-=gap)
                {
                    array[j+gap]=array[j];
                }
                array[j+gap]=temp;
            }
        }
    }while(gap>1);  //~~~~~~~~~~~~~~~~~~~~~~

}

5)直接选择排序

从所有记录中选择出最小的跟第一个数字交换位置,然后又从剩下来的记录中选择最小的跟第二个数字交换位置。

稳定性:不稳定    平均时间复杂度:O(n^2)  

//array[]为待排序数组,n为数组长度
void selectSort(int array[], int n)
{
    int i, j ,min ,k;
    for( i=0; i<n-1; i++)
    {
        min=i; //每趟排序最小值先等于第一个数,遍历剩下的数
        for( j=i+1; j<n; j++) //从i下一个数开始检查
        {
            if(array[min]>array[j])
            {
                min=j;
            }
        }
        if(min!=i)
        {
            k=array[min];
            array[min]=array[i];
            array[i]=k;
        }
    }
}

6)堆排序

稳定性:不稳定   平均时间复杂度:O(nlogn)

堆排序从小到大排序:首先将数组元素建成大小为n的大顶堆,堆顶(数组第一个元素)是所有元素中的最大值,将堆顶元素和数组最后一个元素进行交换,再将除了最后一个数的n-1个元素建立成大顶堆,再将最大元素和数组倒数第二个元素进行交换,重复直至堆大小减为1。

注:完全二叉树 
假设二叉树深度为n,除了第n层外,n-1层节点都有两个孩子,第n层节点连续从左到右。如下图
è¿éåå¾çæè¿°

注:大顶堆 
大顶堆是具有以下性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值。 
即,根节点是堆中最大的值,按照层序遍历给节点从1开始编号,则节点之间满足如下关系: 
这里写图片描述 (1<=i<=n/2)

void heapSort(int array[], int n)
{
    int i;
    for (i=n/2;i>0;i--)
    {
        HeapAdjust(array,i,n);//从下向上,从右向左调整
    }
    for( i=n;i>1;i--)
    {
        swap(array, 1, i);
        HeapAdjust(array, 1, i-1);//从上到下,从左向右调整
    }
}
void HeapAdjust(int array[], int s, int n )
{
    int i,temp;
    temp = array[s];
    for(i=2*s;i<=n;i*=2)
    {
        if(i<n&&array[i]<array[i+1])
        {
            i++;
        }
        if(temp>=array[i])
        {
            break;
        }
        array[s]=array[i];
        s=i;
    }
    array[s]=temp;
}
void swap(int array[], int i, int j)
{
    int temp;

    temp=array[i];
    array[i]=array[j];
    array[j]=temp;
}

7)归并排序

每次选择2^n数进行归并排序,n从0开始,一直到2^n大于等于整个待排序数列。

稳定性:稳定   平均时间复杂度:O(nlogn)

void MergeSort(int k[],int n)  
{  
    int i,next,left_min,left_max,right_min,right_max;  
    //动态申请一个与原来数组一样大小的空间用来存储
    int *temp = (int *)malloc(n * sizeof(int));  
    //逐级上升,第一次比较2个,第二次比较4个,第三次比较8个。。。  
    for(i=1; i<n; i*=2)  
    {  
        //每次都从0开始,数组的头元素开始  
        for(left_min=0; left_min<n-i; left_min = right_max)  
        {  
            right_min = left_max = left_min + i;  
            right_max = left_max + i;  
            //右边的下标最大值只能为n  
            if(right_max>n)  
            {  
                right_max = n;  
            }  
            //next是用来标志temp数组下标的,由于每次数据都有返回到K,  
            //故每次开始得重新置零  
            next = 0;  
            //如果左边的数据还没达到分割线且右边的数组没到达分割线,开始循环  
            while(left_min<left_max&&right_min<right_max)  
            {  
                if(k[left_min] < k[right_min])  
                {  
                    temp[next++] = k[left_min++];  
                }  
                else  
                {  
                    temp[next++] = k[right_min++];  
                }  
            }  
            //上面循环结束的条件有两个,如果是左边的游标尚未到达,那么需要把  
            //数组接回去,可能会有疑问,那如果右边的没到达呢,其实模拟一下就可以  
            //知道,如果右边没到达,那么说明右边的数据比较大,这时也就不用移动位置了  

            while(left_min < left_max)  
            {  
                //如果left_min小于left_max,说明现在左边的数据比较大  
                //直接把它们接到数组的min之前就行  
                k[--right_min] = k[--left_max];   
            }  
            while(next>0)  
            {  
                //把排好序的那部分数组返回该k  
                k[--right_min] = temp[--next];        
            }  
        }  
    }  
}  
//非递归的方法,避免了递归时深度为log2N的栈空间,
//空间只是用到归并临时申请的跟原来数组一样大小的空间,并且在时间性能上也有一定的提升,
//因此,使用归并排序是,尽量考虑用非递归的方法。

8)基数排序

把待排序数列按个位分别按顺序放入0--9的桶中,然后拿出来,再按十位、百位。。。

稳定性:稳定   平均时间复杂度:O(dn)

//基数排序  
//LSD  先以低位排,再以高位排  
//MSD  先以高位排,再以低位排  
void LSDSort(int *a, int n)  
{  
    assert(a);  //判断a是否为空,也可以a为空||n<2返回
    int digit = 0;   //最大位数初始化
    for (int i = 0; i < n; ++i)  
    {   //求最大位数
        while (a[i] > (pow(10,digit)))  //pow函数要包含头文件math.h,pow(10,digit)=10^digit
        {  
            digit++;  
        }  
    }  
    int flag = 1;   //位数
    for (int j = 1; j <= digit; ++j)  
    {  
        //建立数组统计每个位出现数据次数(Digit[n]为桶排序b[n])  
        int Digit[10] = { 0 };  
        for (int i = 0; i < n; ++i)  
        {  
            Digit[(a[i] / flag)%10]++;  //flag=1时为按个位桶排序
        }  
         //建立数组统计起始下标(BeginIndex[n]为个数累加c[n],用于记录重复元素位置
         //flag=1时,下标代表个位数值,数值代表位置,跳跃代表重复)
        int BeginIndex[10] = { 0 };  
        for (int i = 1; i < 10; ++i)  
        {  
            //累加个数
            BeginIndex[i] = BeginIndex[i - 1] + Digit[i - 1];  
        }  
        //建立辅助空间进行排序 
        //下面两条可以用calloc函数实现
        int *tmp = new int[n];  
        memset(tmp, 0, sizeof(int)*n);//初始化  
        //联合各数组求排序后的位置存在temp中
        for (int i = 0; i < n; ++i)  
        {  
            int index = (a[i] / flag)%10;  //桶排序和位置数组中的下标
            //计算temp相应位置对应a[i]中的元素,++为BeginIndex数组数值加1
            //跳跃间隔用++来补,先用再++
            tmp[BeginIndex[index]++] = a[i];  
        }  
        //将数据重新写回原空间  
        for (int i = 0; i < n; ++i)  
        {  
            a[i] = tmp[i];  
        }  
        flag = flag * 10;  
        delete[] tmp;  
    }  
}  

 

图片都来自:排序算法时间复杂度、空间复杂度、稳定性比较

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值