IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 排序的深度剖析 -> 正文阅读

[数据结构与算法]排序的深度剖析

文章目录

  • 系列文章目录
  • 1.插入排序:(1)直接插入排序
  • 2.选择排序:(1)选择排序
  • ? ? ? ? ? ? ? ? ? ? ? ?(2)堆排序
  • 3.交换排序? ? ?(1)冒泡排序
  • ? ? ? ? ? ? ? ? ? ? ? ? ?(2)快速排序
  • 4.归并排序
  • ? ? ? ? ? ? ? ? ? ? ? ??

这里所有的排序都是指从小到大的排序

稳定性:

指排序后相同元素的 相对位置不变

1.直接插入排序

简而言之就是给每个数据找一个合适的位置给放下

时间复杂度为:O(N2)

空间复杂度为:O(1)

稳定性:稳定

public static void insert(int[]arr,int left,int right)
    {
        int i=0;
        for(i=left+1;i<=right;i++)
        {
            int j=i-1;
            int tmp=arr[i];
            while(j>=0)
            {
                if(arr[i]<arr[j])
                {
                    arr[j+1]=arr[j];
                    j--;
                }
                else
                {
                    arr[j+1]=tmp;
                }
            }
            arr[0]=tmp;
        }
    }

2.选择排序

时间复杂度为O(N2)

空间复杂度为O(1)

稳定性:不稳定

(1)选择排序

? ? 一共两种思路

第一种思路是遍历数组:依次找到最小的数据把它放在前面

public static void seletSort(int[] arr) {
        int i = 0;
        for (i = 0; i < arr.length; i++) {


            //int j = i + 1;
            int min = i;
            for(int j=i+1;j<arr.length;j++) {
                if(arr[j]<arr[min])
                {
                    min=j;
                }



            }


            if (min != i) {
                int tmp = arr[min];
                arr[min] = arr[i];
                arr[i] = tmp;
            }

        }
    }

第二种思路是

从前往后找最大的,从后往前找最小的,然后把最小的放在最左边,然后把最大的放在最右边,依次类推

public static void seletsort2(int[]arr)
    {
        int left=0;
        int right=arr.length-1;
        while(left<right)
        {
            int min=left;
            int max=left;
            int i=0;
            for(i=left+1;i<=right;i++)
            {
                if(arr[i]<arr[min])
                {
                    min=i;

                }
                if(arr[i]>arr[max])
                {
                    max=i;
                }
            }
            swap(arr,min,left);
            if(max==left)
            {
                max=min;
            }
            swap(arr,max,right);
            //swap(arr,min,left);
            //arr[left]=arr[min];
            //arr[right]=arr[max];
            left++;
            right--;
        }
    }

这里有一个点我们需要注意:当最前边的元素最大时,

此时left在0位置,right在4的位置,遍历完后此时最小值是1,下标是3,最大值是9在0下标,我们交换后那么最大值就在3?的位置,

此时最大值下标在最小值小标上,我们这时只需将max=min,将最大值下标更新一下就行了。?

3.堆排序

时间复杂度为:O(N*log2N)

空间复杂度:O(1)

稳定性:不稳定

我们可以建立成一个大根堆,然后将头顶元素依次(每个头顶元素都是当前大根堆里最大的元素)和最后一个元素交换

public static void creat()    //如果是从小到大排序的话,就建立一个大根堆
{
    for(int parent=(usesized-1-1)/2;parent>=0;parent--)
    {
        shit(usesized,parent);
    }

}
public static void shit(int len,int parent)
{
    int child=2*parent+1;
    while(child<len)
    {
        if(child+1<len&&elem[child]<elem[child+1])
        {
            child++;

        }


            if(elem[child]>elem[parent])
            {
                int tmp=elem[child];
                elem[child]=elem[parent];
                elem[parent]=tmp;
                parent=child;
                child=2*parent+1;
            }
            else
            {
                break;
            }

    }
public void paixu() {
    while (usesized >= 1) {
        int tmp1 = elem[0];
        elem[0] = elem[usesized - 1];
        elem[usesized - 1] = tmp1;
        usesized--;

        shit(usesized, 0);


    }
}

3.交换排序:

(1)冒泡排序:

? ? 时间复杂度为O(N2)

? ? 空间复杂度为O(1)

? ? ?稳定性:稳定

??

 public static void maopao(int[]arr)
    {
       int i=0;
       for(i=0;i<arr.length-1;i++)
       {
           int j=0;
           boolean a=false;
           for(j=0;j<arr.length-1-i;j++)
           {
               if(arr[j]>arr[j+1])
               {
                   swop(arr,j);
                   a=true;
               }
           }
           if(a==false)   //说明此时序列已经有序
           {
               return;
           }
       }
    }

? ?(2)快速排序

? ? ? ? ?时间复杂度:O(N)

? ? ? ? 空间复杂度:O(log2N)

? ? ? ? 稳定性:不稳定

? ? ? ? ? ? Hore版

? ? ? ? ?

先把6作为基准,R先从右边找比6大的,找到停下,然后L再从左边找,找到比6?小的,然后交换,然后R再找找完L再找,直到两者相遇,此时将相遇点的值和基准交换,此时我们可以观察到6的左边都是比6小的,6的右边都是比6大的,然后我们分而治之

直到l r重合,返回。

public static void qiuklysort(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        int pre=find1(arr,left,right);
        qiuklysort(arr,left,pre-1);
        qiuklysort(arr,pre+1,right);


    }
public static int find1(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            arr[left]=arr[right];
            //left++;                              //注意这里的left下面的right--,一旦加上了就不存可能永不相遇的情况
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
            arr[right]=arr[left];
            //right--;

        }
        arr[left]=tmp;
        return left;
    }

?2.挖孔法:

最后相遇的时候把6在放入这个坑位。

public static void quklysort2(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }

       int b= findmid(arr,left,right);
        //int tmp=arr[b];
        //arr[b]=arr[left];
        //arr[left]=tmp;
        int pre=find1(arr,left,right);
        quklysort2(arr,left,pre-1);
        quklysort2(arr,pre+1,right);


    }
public static int find1(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            arr[left]=arr[right];
            //left++;
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
            arr[right]=arr[left];
            //right--;

        }
        arr[left]=tmp;
        return left;
    }

?我们在解题的时候先考虑挖坑法,再用Hore版

快排的优化

我们知道数据是越来越有序的,那么我们如果在递归多的区间进行选择排序,那么快排就会变快

像这个其实递归多的地方主要在下边?

 public static void quklysort4(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        if(right-left>=2)
        {
            selectsort3(arr,left,right);
        }
        int pre=find1(arr,left,right);
        qiuklysort(arr,left,pre-1);
        qiuklysort(arr,pre+1,right);
    }

像这样一组数据,我们如果还是始终以第一个数据为基准的话,就会排成这样

时间复杂度也会变为O(N2),空间复杂度会变为O(N)那么什么办法去解决这个问题吗?

我们用三数取中法来解决这个问题:

我们将left 、right? 以及? (left+right?)/2下标的数值进行比较,找到第二个大的数将它作为基准,这样就可以使基准左右都有数据。

ublic static void quklysort2(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        
       int b= findmid(arr,left,right);
        //int tmp=arr[b];
        //arr[b]=arr[left];
        //arr[left]=tmp;
        int pre=find1(arr,left,right);
        quklysort2(arr,left,pre-1);
        quklysort2(arr,pre+1,right);


    }
    public static int findmid(int[]arr,int left,int right)
    {
        int mid=(left+right)/2;
        if(arr[left]>arr[right])
        {
            if(arr[mid]<arr[left])
            {
                return right;
            }
            else if(arr[mid]>arr[left])
            {
                return left;
            }
            else
            {
                return mid;
            }
        }
        else
        {
            if(arr[mid]<arr[left])
            {
                return left;
            }
            if(arr[mid]>arr[right])
            {
                return right;
            }
            return mid;
        }
    }

?快排的非递归方法

建立一个栈,排序找基准,然后将基准两侧的left,right分别放入栈中,(这里要注意当基准一侧只有一个元素时,说明基准的这一侧已经有序,这一侧的left和right不需要再放入栈中),然后每次再弹出两个坐标,再后再排序,找基准再将基准放入栈中,再弹出,以此类推,直到栈为空

?

 public static void fei(int[]arr)
    {
        Stack<Integer>stack=new Stack<>();
        int left=0;
        int right=arr.length-1;
        int mid=find(arr,left,right);
        if(left<mid-1)
        {
            stack.push(left);
            stack.push(mid-1);

        }
        if(mid<right-1)
        {
            stack.push(mid+1);
            stack.push(right);
        }
        while(!stack.isEmpty())
        {
             right=stack.pop();
             left=stack.pop();
            mid=find(arr,left,right);
            if(left<mid-1)
            {
                stack.push(left);
                stack.push(mid-1);

            }
            if(mid<right-1)
            {
                stack.push(mid+1);
                stack.push(right);
            }
        }
    }
    public static int find(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        int i=left;
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
            swop(arr,left,right);

        }
        swop(arr,left,i);
        return left;

    }
    public static void swop(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        arr[left]=arr[right];
        arr[right]=tmp;
    }

?归并排序:

时间复杂度O(N*logN)

空间复杂度O(N)

稳定性:稳定

合并排序的思路就是:先把序列一步步的分解,分解成单个,然后再返回,再将子序列逐步地排序

最后就能形成有序的序列

//归并public static void fen(int[]arr,int left,int right)
{
    if(left==right)
    {
        return ;
    }
    int mid=(left+right)/2;
    fen(arr,left,mid);
    fen(arr,mid+1,right);
    paixu(arr,left,mid,right);
}
public static void paixu(int[]arr,int left,int mid,int right)
{
    int[]arr2=new int[right-left+1];
    int i=left;
    int j=mid+1;
    int k=0;
    while(i<=mid&&j<=right)
    {
        if(arr[i]<=arr[j])
        {
            arr2[k++]=arr[i++];
        }
        else
        {
            arr2[k++]=arr[j++];
        }
    }
    while(i<=mid)
    {
        arr2[k++]=arr[i++];
    }
    while(j<=right)
    {
        arr2[k++]=arr[j++];
    }
    for(i=0;i<k;i++)
    {
        arr[i+left]=arr2[i];
    }


}

不用比较的排序:

计数数组:(适用于集中的数)

举个例子假设有一个数组arr里面都是0-9的数,那么如果我们再定义一个大小为10的数组,然后把arr里面的每个数当成计数数组的下标,计数数组每次下标相同时这个下标元素的值都要加加

假设int[]arr={0,0,1,3,2,1,4,5,5,5,6,7,8,9};

我们在定义一个计数数组

?

每个count[i]都表示arr数组这个下标的元素有几个,这样我们就按照下标训序打印,但是注意个数,比如0 有两个,就打印两次

//计数排序public static void count(int[]arr)
{
    int i=0;
    int min=arr[0];
    int max=arr[0];
    for(i=1;i<arr.length;i++)
    {
        if(arr[i]<min)
        {
            min=arr[i];
        }
        if(arr[i]>max)
        {
            max=arr[i];
        }

    }
    int size=max-min+1; //确定计数数组的大小
    int[]arr2=new int[size];
    for(i=0;i<arr.length;i++)
    {
        arr2[arr[i]-min]++;//处理如果数据不是0-9这样类似的数据,这时让每个数据减去最小值就0k
    }
    for(i=0;i<size;i++)
    {
        while(arr2[i]>=1)
        {

            System.out.print(i+min+" ");//打印的时候再加上min就变成原数了
            arr2[i]--;
        }


    }


}


?

??


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:39:02  更:2022-10-22 21:39:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:27:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码