首页 > 编程语言 >数据结构-排序算法(Java实现)

数据结构-排序算法(Java实现)

时间:2024-03-27 12:00:39浏览次数:29  
标签:排序 Java int mid right array 数据结构 left

1. 插入排序

1.1 基本思想

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

1.2 图解


 

1.3 原理解析

第一趟: 一组数据可以分为有序序列无序序列 , i 表示无序序列的第一个元素,j 表示有序序列的最后一个元素,用 i 下标的元素和 j 下标的元素进行比较,满足条件则交换,j-- 不断往后退,i 逐个比较,比较到 j < 0 停止

  • 具体的交换条件:
  1.  先把  i 下标的元素赋值给中间变量(int tmp = arr[i] )
  2.  当  j 下标的元素大于中间变量时,把 j 下标元素赋值到 j+1 的位置,然后中间变量继续依次与 j-- 后所在下标的位置进行比较,直到 j < 0 后停止。
  3.  当 j 下标的元素小于或等于中间变量时,直接把中间变量的元素放到 j+1 的位置即可(也就是原来的位置)

第二趟:i++ 重复上述步骤,直到最后一个元素

1.4 代码实现

    public static void inserSort(int[] array) {
        for (int i = 1; i < array.length; i++) {  //i从1下标开始遍历整个数组
            int tmp = array[i]; //创建一个变量存储数值
            int j = i - 1;  //j的下标位置
            for (; j >= 0; j--) {
                if (array[j] > tmp) { //如果下标j大于变量的值,则需要调整
                    array[j + 1] = array[j];  //放到j+1所在的位置
                } else {
                    //array[j+1] = tmp; //变量放回原位;
                    break; //跳出
                }
            }
            array[j + 1] = tmp; //变量放回原位;
        }
    }

2. 希尔排序(缩小增量排序)

2.1 基本思想

        先选定一个整数,把待排序文件中所有记录分成多个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

2.2 图解

2.3 代码实现

    //希尔排序 不稳定
    public static void shellSort(int[] array) {
        int gap = array.length; //根据元素个数分组数 例如 10
        while (gap > 1) {
            gap /= 2; //5,2,1
            shell(array, gap);
        }
    }

    private static void shell(int[] array, int gap) {
        for (int i = gap; i < array.length; i++) {
            int tmp = array[i];
            int j = i - gap;
            for (; j >= 0; j = j - gap) {  //因为间隔gap个数,所以j要减gap
                if (array[j] > tmp) {  //j下标元素大于tmp变量时,就插入排序交换
                    array[j + gap] = array[j];
                } else {
                    break;
                }
            }
            array[j + gap] = tmp;
        }
    }

 3. 选择排序

3.1 基本思想

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

3.2 图解


 

3.3 代码实现

 //直接选择排序
    //时间复杂度:O(N^2)
    //空间复杂度:O(1)
    //稳定性:不稳定
    public static void selectSort(int[] arrary) {
        for (int i = 0; i < arrary.length; i++) {
            int minIndex = i;  //先把i确定为最小值的下标,后续如果碰到比它小的数值就更新minIndex
            int j = i + 1;  //确定j下标的位置
            for (; j < arrary.length; j++) {
                if (arrary[minIndex] > arrary[j]) {
                    //min一定存储最小值的下标
                    minIndex = j;  //J下标的值更小就更新minIndex的值
                }
            }
            //走到这一步就说明这一趟minIndex存储最小值的下标,然后就可以和i下标的值进行交换
            swap(arrary, i, minIndex);
        }
    }

    private static void swap(int[] array, int i, int j) {
        //交换
        int tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
    }

双向排序算法(了解)

    public static void selectSort2(int[] arrary) {
        int left = 0;
        int right = arrary.length - 1;
        while (left < right) {
            int minIndex = left;
            int maxIndex = right;
            for (int i = left + 1; i < right; i++) {
                if (arrary[i] < arrary[minIndex]) {
                    minIndex = i;
                }
                if (arrary[i] > arrary[maxIndex]) {
                    maxIndex = i;
                }
            }

            if (maxIndex == left) {
                maxIndex = minIndex;
            }
            swap(arrary, minIndex, left);
            swap(arrary, maxIndex, left);
            left++;
            right--;
        }
    }

    private static void swap(int[] array, int i, int j) {
        //交换
        int tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
    }

4. 堆排序

4.1 基本思想

堆是一种数据结构,可以把堆看成一棵完全二叉树,这棵完全二叉树满足:任何一个非叶结点的值都不大于(或不小于)其左右孩子结点的值。若父亲大孩子小,则这样的堆叫作大根堆;若父亲小孩子大,则这样的堆叫作小根堆

根据堆的定义知道,代表堆的这棵完全二叉树的根结点的值是最大(或最小)的,因此将一个无序序列调整为一个堆,就可以找出这个序列的最大(或最小)值,然后将找出的这个值交换到序列的最后(或最前),这样,有序序列关键字增加1个,无序序列中关键字减少1个,对新的无序序列重复这样的操作,就实现了排序。这就是堆排序的思想。

堆排序中最关键的操作是将序列调整为堆。整个排序的过程就是通过不断调整,使得不符合堆定义的完全二叉树变为符合堆定义的完全二叉树

4.2 执行过程

建堆是先从自下而上,从右往左

初始堆的每一个结点都要满足堆的定义,也就是父节点的值大于或小于左右孩子结点的值!!!

选出最大值,是将根结点和最后一个结点互换,然后继续构建大顶堆!!!

⭐⭐⭐堆顶和最后一个元素交换,才算一趟,也是该趟的最终序列结果!!!

建堆和排序结果是两个阶段,但同属于一趟中。

4.3 代码实现

    //堆排序
    public void heapSort(int[] array) {
        creatHeap((array));  //建堆
        int end = array.length - 1;  //确定最后一个结点的下标
        //当只剩下一个结点的时候就不用交换
        while (end > 0) {
            //交换
            swap(array, 0, end);
            //向下调整
            siftDown(array, 0, end);
            //调整完一个结点,减1
            end--;
        }

    }

    private void creatHeap(int[] array) {
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            siftDown(array, parent, array.length);
        }
    }
    //向下调整
    private void siftDown(int[] array, int parent, int len) {
        int child = (2 * parent) + 1;  //得到左孩子结点的下标
        while (child < len) {  //小于数组长度说明没调完
            if (child + 1 < len && array[child] < array[child + 1]) {
                //child+1小于usedsize说明没有越界
                //判断左孩子如果比右孩子小的话,child就要指向右孩子,因为下一步要用最大的孩子结点和父亲结点交换
                child = child + 1;
            }
            //程序执行到这一步就说明child一定是左右孩子最大值的下标
            if (array[child] > array[parent]) {  //child和parent找出最大值进行交换
                swap(array, child, parent);
                parent = child;  //继续向下调整
                child = 2 * parent + 1;
            } else {
                break;  //不用交换
            }
        }
    }

 5. 冒泡排序

5.1 基本思想

左边大于右边就交换,一趟排下来最大的放在右边

5.2 图解

5.3 代码实现

    public static void bubbleSort(int[] array) {
        //i代表趟数 10个数据比较的是9趟
        for (int i = 0; i < array.length - 1; i++) {
            boolean flg = false;
            //每一趟从下标0开始比较,每一趟比较都会少i+1次
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    swap(array, j, j + 1);
                    flg = true;
                }
            }
            //如果没有交换就证明是有序的
            if (flg == false) {
                break;
            }
        }
    }

6. 快速排序⭐⭐⭐⭐⭐

6.1 基本思想

快速排序时"交换"类的排序,运用了分治的思想,通过一趟排序把将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,直到整个数据变成有序序列。

快速排序算法通过多次的比较和交换来实现排序,排序步骤如下:

  1. 首先从待排序列中选择一个数作为基准值(mid),通过该基准值把数组分成左右两部分。
  2. 大于或等于基准值的数据集中到数组的右边小于基准值的数据集中到数组的左边
  3. 采用分治思想,对左右两个区间数组分别采用上述的方式选择一个数作为基准值,每个区间采用同样的方式把数据分为左右两部分,同样在左边是小于等于该基准值的,右边是大于该基准值的。
  4. 重复上述过程,可以看出是一个递归定义。通过递归将左侧部分排好序后,再递归排右侧部分的顺序,当左、右两个部分都排序好后,整个数组排序都排完了。

6.2 图解

6.3 挖坑法

6.3.1 代码实现

    public static void quickSort(int[] array) {  //为了统一排序算法接口
        quick(array, 0, array.length - 1);
    }

    private static void quick(int[] array, int start, int end) {
        if (start >= end) { //防止end到start后面越界,所以>=
            return;
        }
        //1.三数取中  index是中间大的数字的下标
        int index = middleNum(array, start, end);
        swap(array, index, start);

        int pivot = partition(array, start, end);  //先划分
        
        quick(array, start, pivot - 1); //递归左边

        quick(array, pivot + 1, end);  //递归右边

    }

    private static int middleNum(int[] array, int left, int right) {
        int mid = left + ((right - left) >> 1);

        if (array[left] < array[right]) {
            if (array[mid] < array[left]) {
                return left;    //例:x<3<9
            } else if (array[mid] > array[right]) {
                return right;
            } else {
                return mid;
            }
        } else {  //左边大于右边的
            if (array[mid] < array[left]) {
                return right;    //例:6>4>x
            } else if (array[mid] > array[right]) {
                return left;
            } else {
                return mid;
            }
        }
    }

 6.4 Hoare法

思路:设置两个下标指针 i 和 j ,j向左走,找到比基准值(pivot)小的值先停下来,不交换,i向右走,找到比基准值(pivot)大的值先停下来,然后交换array[i] 和 array[j],当i和j相遇后,交换array[i] 和 array[left]。

6.4.1 代码实现

    private static int partitionHoare(int[] array, int left, int right) {
        int tmp = array[left]; // 找一个基准
        int i = left;  //记录left的下标
        while (left < right) {  //直到相遇后退出循环
            while (left < right && array[right] >= tmp) {  //从最右边开始依次找个小于基准的数,
                right--;               //因为会出现右边的数全部大于基准数,right--会出现数组越界的情况,所以再加个left<right条件
            }

            while (left < right && array[left] <= tmp) {  //从最左边开始依次找个大于基准的数
                left++;
            }

            swap(array, left, right);  //经过上述程序步骤得到下标,交换
        }
        swap(array, i, left);  //I下标交换(left和right)下标相遇的位置,只交换值
        return left;  //返回相遇的位置
    }

第二种快速排序,Hoare法,主要是交换的方式不同

    //前后指针法
    private static int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while (left < right) {
            //right往左边走
            while (left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];  //把right指向的值给left指向的位置
            //left往右边走
            while (left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left]; //把left指向的值给right的值
        }
        array[left] = tmp; //把基准放回去
        return left;
    }

6.5 前后指针法(了解)

总体思路:prev一直往后走(prev++),当array[prev]小于array[left]的时候,cur++。当array[cur] != array[prev]时。

6.5.1 代码实现

    //前后指针法
    private static int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while (left < right) {
            //right往左边走
            while (left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];  //把right指向的值给left指向的位置
            //left往右边走
            while (left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left]; //把left指向的值给right的值
        }
        array[left] = tmp; //把基准放回去
        return left;
    }

7. 归并排序

 7.1 基本思想

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

归并排序算法有两个基本的操作,一个是,也就是把原数组划分成两个子数组的过程。另一个是,它将两个有序数组合并成一个更大的有序数组。

  1. 将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。
  2. 将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。

7.2 图解

假设我们有一个初始数列为{8, 4, 5, 7, 1, 3, 6, 2},整个归并排序的过程如下图所示。

分而治之:

7.3 代码实现

7.3.1 递归版本

  //归并排序

    public static void mergeSort(int[] array) {  //为了接口的统一性
        mergeFunc(array, 0, array.length - 1);
    }

    private static void mergeFunc(int[] array, int left, int right) {
        if (left >= right) {
            return;  //证明递归分解完毕
        }
        int mid = left + ((right - left) >> 1); //确定数组中间的元素  (>>1 是除2)

        //递归左边的子序列(分解)
        mergeFunc(array, left, mid);
        //递归右边的子序列(分解)
        mergeFunc(array, mid + 1, right);

        //左边右边分解完可以开始合并
        merge(array, left, mid, right);
    }

    //合并的方法
    private static void merge(int[] array, int left, int mid, int right) {
        //左子序列从left 开始
        int s1 = left;
        int e1 = mid;

        //右子序列从mid+1开始
        int s2 = mid + 1;
        int e2 = right;

        //创建一个新数组作为复制数组
        int[] tmpArr = new int[right - left + 1];
        int k = 0;

        //每次比较 都是S1和S2进行比较
        //2.如果一个表没有了数据,直接把另一个表剩下的数据全部拷贝进新数组

        //1.保证两个表都有数据
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                //s1小的话就放进新数组
                tmpArr[k++] = array[s1++];  //s1放进数组后,新数组k要++,旧数组s1也要++
            } else {
                //反之s2放进新数组
                tmpArr[k++] = array[s2++];
            }
        }

        //2.看哪个数组还有数据,拷贝回去
        while (s1 <= e1) {  //证明s2已经走完了,直接把剩下的复制到新数组
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= e2) {  //证明s1已经走完了,直接把剩下的复制到新数组
            tmpArr[k++] = array[s2++];
        }

        //把在新数组排好序的拷贝回原数组
        for (int i = 0; i < k; i++) {
            array[i + left] = tmpArr[i];  //拷贝回原数组要+left(下标不一定是从0开始的)
        }
    }

7.3.2 非递归版本

 

    //非递归实现归并排序
    public static void mergeSortNor(int[] array) {
        int gap = 1;
        //最外层控制组数
        while (gap < array.length) {
            //每一层进行排序
            for (int i = 0; i < array.length; i = i + 2 * gap) {
                int left = i;
                int mid = left + gap - 1;
                if(mid >= array.length) {  //预防越界
                    mid = array.length-1;
                }
                int right = mid + gap;
                if(right >= array.length) {  //预防越界
                    right = array.length-1;
                }

                merge(array,left,mid,right);
            }

            gap *= 2;
        }
    }

标签:排序,Java,int,mid,right,array,数据结构,left
From: https://blog.csdn.net/cj13106811623/article/details/136780136

相关文章

  • Oracle 分页查询,排序分页
    效率最高内查询小于等于外查询大于select*from(selectt.*,rownumasnfromSTUDENTtwhererownum<=4)twheret.n>2orderbyt.iddesc;查看执行计划explainplanforselect*from(selectrownumasn,d.*fromdeptdwhererownum<=4)twheret.n>......
  • Java8递归生成树
    @Data@BuilderpublicclassMenu{/***id*/publicIntegerid;/***名称*/publicStringname;/***父id,根节点为0*/publicIntegerparentId;/***子节点信息*/publicList<Menu>......
  • Java集合排序
    packagecom.example.demo;importjava.util.ArrayList;importjava.util.Collections;importjava.util.Comparator;importjava.util.List;publicclassTestApp{/**Comparable是一个内部比较接口,通常对象需要内部排序时直接实现Comparator是一个外......
  • java毕业设计基于微信小程序的图书馆预约[附源码]
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义选题背景:随着移动互联网技术的飞速发展,微信小程序凭借其无需下载安装、使用方便的特点,已经成为人们日常生活中不可或缺的一部分。图书馆作为知识传播的重要场所,在......
  • Java语言程序设计2
    第二章、变量、数据变量、运算符、表达式一、变量1.概念:计算机中的一块内存空间,存储数据的基本单元2.变量的组成部分:数据类型,变量名,数据3.语法:(1)先声明,在赋值:    数据类型变量名;//声明    变量名=值;//值(2)声明同时并赋值:    数据类型变量......
  • java的封装
    封装概述    java中的封装指的是将一系列有关的事物的共同属性和行为提取出来放到一个类中,隐藏对象的实行和现实细节,仅对外提供公共的访问方式的操作。这样说起来感觉很抽象,也不好理解,这里不妨举一个例子。将配置电脑这个动作看成封装。    这个要怎么理解呢......
  • Java进阶 - [1-4] 反射
      一、类加载区别当我们刚接触java语言的时候,我们最常见的代码应该就是初始化某个对象,然后调用该对象的方法。1、使用new创建对象,返回对象的引用。Studentstudent=newStudent();2、调用方法:student.say(); 当我们想在运行期才能指定具体对象的类型或调用的某个方法......
  • 浏览器工作原理与实践--渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的
    在上篇文章中,我们介绍了渲染流水线中的DOM生成、样式计算和布局三个阶段,那今天我们接着讲解渲染流水线后面的阶段。这里还是先简单回顾下上节前三个阶段的主要内容:在HTML页面内容被提交给渲染引擎之后,渲染引擎首先将HTML解析为浏览器可以理解的DOM;然后根据CSS样式表,计算出DOM......
  • 浏览器工作原理与实践--渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的
    在上一篇文章中我们介绍了导航相关的流程,那导航被提交后又会怎么样呢?就进入了渲染阶段。这个阶段很重要,了解其相关流程能让你“看透”页面是如何工作的,有了这些知识,你可以解决一系列相关的问题,比如能熟练使用开发者工具,因为能够理解开发者工具里面大部分项目的含义,能优化页面卡......
  • Java使用AES加密
    publicclassAESUtil{publicstaticfinalStringalgorithm="AES";//AES/CBC/NOPaddin//AES默认模式//使用CBC模式,在初始化Cipher对象时,需要增加参数,初始化向量IV:IvParameterSpeciv=new//IvParameterSpec(key.getBytes());/......