Java数据结构与算法(冒泡排序,选择排序,插入排序,希尔排序,快速排序,归并排序,基数排序)

排序的分类
(1)内部排序法:指将需要处理的所有数据都i加载到内部存储器(内存)中进行排序。
(2)外部排序法:数据量过大,无法就全部加载到内存中,需要借助外部存储(文件等)进行排序。
在这里插入图片描述
算法的时间复杂度
度量一个程序(算法)执行时间的两种方法
(1)事后统计法
这种方法可行,但是有两个问题:一是想要对设计的算法的运行新能进行评测,需要实际运行该程序;二是所得时间的统计量依赖于计算机的硬件,软件等环境因素,这种方式,要是同一台计算机的相同状态下运行,才能比较那个栓发熟读更块。
(2)事前估算的方法
通过分析某个算法的时间复杂度来判断那个算法更优

常见的时间复杂度
1)常数阶O(1)
2)对数阶O(log2n)
3)线性阶O(n)
4)线性对数阶O(nlog2n)
5)平方阶O(n^2) 6)立方阶O(n^3)
7)K次方阶O(n^k) 8)指数阶O(2^n)
常见的算法时间复杂度由小到大依次为:O(1) < O(log2n) < O(n) < O(nlog2n) < O(n^2) < O(n^3) < O(n^k) < O(2^n)

平均时间复杂度和最坏时间复杂度

排序法 平均时间 最差情形 稳定度 额外空间 备注
冒泡 O(n^2) O(n^2) 稳定 O(1) n小较好
交换 O(n^2) O(n^2) 不稳定 O(1) n小较好
选择 O(^2) O(n^2) 不稳定 O(1)n小较好
插入 O(n^2) O(n^2) 稳定 O(1) 大部分已排序时较好
基数 O(logRB) O(logRB) 稳定 O(n) B是真数(0-9),R是基数(个十百)
Shell(希尔) O(nlogn) O(n^s)1 < s< 2 不稳定 O(1) s是所选分组
快速 O(nlogn) O(n^2) 不稳定 O(nlogn) n大较好
归并 O(nlogn) O(nlogn) 稳定 O(1) n大较好
O(nlogn) O(nlogn) 不稳定 O(1) n大较好

冒泡排序

冒泡排序(Bubble
Sorting)基本思想:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部。

算法步骤:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
在这里插入图片描述
代码实现:

 public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    1,8,2,5,7,4,3,9};
        int length = arr.length;
        //创建变量来减少不必要的循环,减小时间复杂度
        boolean flag = false;
        //从小到大进行排序
        for(int i = 1;i < length; i++) {
    
    
            flag = false;
            for(int j = 0;j < length-i;j++) {
    
    
                if(arr[j] > arr[j+1]) {
    
    
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                    flag = true;
                }
            }
            if(!flag) {
    
    
                break;
            }
        }
    }

选择排序

选择排序(select sorting)基本思想:第一次从arr[0] ~
arr[n-1]中选取最小值与arr[0]交换;第二次从arr[1] ~
arr[n-1]中挑选最小值与arr[1]交换;第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i] ~ arr[n-1];…,第n-1次从arr[n-1] ~
arr[n-1]中选出最小值与arr[n-2]交换,总共通过n-1次,得到一个按照从小到大排序的有序序列

算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
在这里插入图片描述
代码 实现:

 public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    1,8,2,5,7,4,3,9};
        int length = arr.length;
        for(int i = 0 ;i < length-1;i++) {
    
    
        	//每次查找之前将最小值的下标设置为i
            int minFlag = i;
            for(int j = i+1;j < length;j++) {
    
    
                if(arr[j] < arr[minFlag]) {
    
    
                    //记录下每一轮循环的最小数值的下标
                    minFlag = j;
                }
            }
            //一次大循环结束,将这轮的第一个数据和最小的数据位置进行交换
            int temp = arr[i];
            arr[i] = arr[minFlag];
            arr[minFlag] = temp;
        }
    }

插入排序

插入排序(Insertion
Sorting)基本思想:把n个待排序的元素看成为一个有序和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排过程中每次从无序标中取出第一个元素,把它的排序码依次与有序表元素的排序码经行比较,将它插入到有序表中的适当位置,使之成为新的有序表

算法步骤
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
在这里插入图片描述
代码实现:

 public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    1,8,2,5,7,4,3,9};
        int length = arr.length;
        for(int i = 1;i < length;i++) {
    
    
            //记录下目前要进行比较的数据
           int temp = arr[i];
           //记录下目前数据的位置
           int j = i;
           //如果下标大于零,并且此下标的前一个元素大于要调换的元素,那么就把元素都往后移,因为之前的都一斤排好了,所以不用在考虑前面的顺序
           while(j > 0 && temp > arr[j-1]) {
    
    
                arr[j] = arr[j-1];
                j--;
           }
           //如果最终找到的位置
           if(j != i) {
    
    
               arr[j] = temp;
           }
        }
    }

希尔排序

希尔排序也是一种插入排序,它是简单擦汗如排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

希尔排序(Donald
Shell)基本思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键字越来越多,。当增量减至1时,整个文件恰被分成一组,便终止算法

算法步骤:
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
在这里插入图片描述
代码实现:

//【交换式】
 public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    1,8,2,5,7,4,3,9};
        int length = arr.length;
        //gap为分组的个数,首次分组为数组总元素个数处于2,后面在继续在目前分组个数上除于2进行分组,一直到分组个数为1个的时候结束分组
        for(int gap = length/2;gap > 0;gap /= 2) {
    
    
            //从每次分组的第一个开始进行循环比较
            for(int i = gap;i < length;i++) {
    
    
                //依次在找到每一组的前几个在前置元素,进行比较插入
                for(int j = i - gap;j >= 0;j -= gap) {
    
    
                    if(arr[j] > arr[j+gap]) {
    
    
                        int temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
        }
    }
//【移位式】
public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    1,8,2,5,7,4,3,9};
        int length = arr.length;
        //每次分组排依次序,一直到分组数量为1为止
        for(int gap = length/2; gap > 0;gap/=2) {
    
    
            //对不同分组进行插入排序
            for(int i = gap;i < length;i++) {
    
    
                int temp = arr[i];
                int j = i;
                while(j-gap >= 0 && temp < arr[j-gap]) {
    
    
                    arr[j] = arr[j-gap];
                    j -= gap;
                }
                if(j != i) {
    
    
                    arr[j] = temp;
                }
            }
        }

快速排序

快速排序(QuickSort)是对冒泡排序的一种改进,基本思想通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行

算法步骤:
1.从数列中挑出一个元素,称为 “基准”(pivot);
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3URMeI31-1636711015598)(https://img-bog.csdnimg.cn/cba219d644ee4a808c0945c31f7d7a3e.gif#pic_center)]

代码实现:

 public  static void quickSort(int[] arr , int left ,int right) {
    
    
        int r = right;//左下标
        int l = left;//右下标
        //中轴值
        int pivot = arr[(r + l)/2];
        //将数组里左边放的都是比pivot小的数,右边都是比pivot大的数
        while(l < r) {
    
    
             //从最左边开始找
             while(arr[l] < pivot) {
    
    
                 l++;
             }
             //从最右边开始找
             while(arr[r] > pivot) {
    
    
                 r--;
             }
             //如果没找到就退出
            if(l >= r) {
    
    
                break;
            }
            //如果没退出就说明找到了,就进行数据的交换
            int temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //交换完后,发现arr[l] == pivot,则就要让r往前移一步
            if(arr[l] == pivot) {
    
    
                r--;
            }
            //交换完后,发现arr[r] == pivot,则就要让l往后移一步
            if(arr[r] == pivot) {
    
    
                l++;
            }
            //如果l == r,那么就要让l++,r--否则会出现栈溢出
            if(l == r) {
    
    
                l++;
                r--;
            }
            //向左递归
            if(left < r) {
    
    
                quickSort(arr,left,r);
            }
            //向右递归
            if(right > l) {
    
    
                quickSort(arr,l,right);
            }
         }

归并排序

归并排序(Merge-Sort)是利用归并的思想进行的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分成一些小问题然后递归求解,而治的阶段将分的阶段得到的各答案结合在一起,即分而治之)

算法步骤:
1,申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

2,设定两个指针,最初位置分别为两个已经排序序列的起始位置;

3,比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

4,重复步骤 3 直到某一指针达到序列尾;

5,将另一序列剩下的所有元素直接复制到合并序列尾。
在这里插入图片描述
代码实现:


    public static void Mergesort(int[] arr,int left,int right,int[] temp) {
    
    
        if(left < right) {
    
    
            int mid = (left + right) / 2;
            //分割左边的数组
            Mergesort(arr, left, mid, temp);
            //分割右边的数组
            Mergesort(arr,mid+1,right,temp);
            //合并
            Merge(arr,left,mid,right,temp);
        }
    }

    public static void Merge(int[] arr,int left,int mid,int right,int[] temp) {
    
    
        int l = left;
        int j = mid + 1;
        int t = 0;

        while(l <= mid && j <= right) {
    
    
            //左边有序序列的元素比右边的有序序列的元素小,则拷贝左边的元素到temp数组,相反则拷贝右边的元素
            if(arr[l] < arr[j]) {
    
    
                temp[t++] = arr[l++];
            }else {
    
    
                temp[t++] = arr[j++];
            }
        }
        //如果左边的元素没有拷贝完,则将其剩下元素进行拷贝
        while(l <= mid) {
    
    
            temp[t++] = arr[l++];
        }
        //如果右边的元素没有拷完,则将右边的剩下元素全部拷贝
        while(j <= right) {
    
    
            temp[t++] = arr[j++];
        }
        //将temp数组的元素拷贝到arr数组
         t = 0;
        int tempLeft = left;
        while(tempLeft <= right) {
    
    
            arr[tempLeft++] = temp[t++];
        }
    }

基数排序

基数排序基本思想:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零,然后,从最低位开始,依次进行依次排序,这样从最低位排序一直到最高位,排序完成以后,数列就变成了一个有序数列

基数排序说明:
(1)基数排序是对传统的桶排序的扩展,速度很快
(2)基数排序是经典的空间换时间的方式,占用内存空间很大,当对海量数据排序时,容易造成OutOfMemoryError。
(3)基数排序是稳定的。【注:假定在待排序的记录序列里,存在多个具有相同的关键字的记录,若金经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] =r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍然在r[j]之前,则这种排序算法称之为稳定的,否则称之为不稳定的
(4)有负数的时候尽量不采用基数排序,若要采用则要进行改进
在这里插入图片描述
代码实现:

  public static void main(String[] args) {
    
    
        //创建一个大小为8的数组,斌且为其赋上初值
        int[] arr = {
    
    190,82,25,512,754,4,143,9};
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void radixSort(int[] arr) {
    
    
        //找到待排数组里面位数最大的数
        int max = arr[0];
        for(int i = 1;i < arr.length;i++) {
    
    
            if(arr[i] > max) {
    
    
                max = arr[i];
            }
        }
        //确定最大位数的数的位数
        int len = (max+"").length();
        //记录放入每个同里面的数据有那些
        int[][] bucket = new int[10][arr.length];
        //记录放入每个桶里的数据的个数
        int[] buckNumber = new int[10];
        //按照不同的位数来进行桶排序
        for(int i = 0,n = 1;i < len;i++,n *= 10) {
    
    
            //循环数组里面的数据,将其放入桶里
            for (int j = 0; j < arr.length; j++) {
    
    
                //取出每个数据每个位数上的数据
                int temp = arr[j] / n % 10;
                bucket[temp][buckNumber[temp]] = arr[j];
                //当指定桶里多了一个数据,就在这个桶的数据总数上加一
                buckNumber[temp]++;
            }
            //按照桶的顺序将桶里面的数据取出来
            int temp = 0;
            for (int k = 0; k < buckNumber.length; k++) {
    
    
                if (buckNumber[k] != 0) {
    
    
                    for (int j = 0; j < buckNumber[k]; j++) {
    
    
                        arr[temp++] = bucket[k][j];
                    }
                }
                //每次循环结束后需要将存储每个同里元素个数的数组清零
                buckNumber[k] = 0;
            }
        }
    }

堆排序

猜你喜欢

转载自blog.csdn.net/qq_48627750/article/details/121264503