最近在复习一些算法,把自己总结的常用的三个排序算法给大家分享下,大家一起学习,指正。后续还会继续分享。
1.冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡:
// 冒泡排序 大数下沉
public static int [] getArr(int [] arr) {
int temp=0;
if(arr!=null && arr.length!=0){
for (int i = 0; i <arr.length-1 ; i++) {
for (int j=0;j<arr.length-i-1;j++){
if (arr[j]>arr[j+1]) {
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
return arr;
}
return null;
}
// 冒泡排序 小数上浮
public static int [] getLittleNumber(int [] arr) {
int temp=0;
if(arr!=null && arr.length!=0){
for (int i = 0; i <arr.length-1 ; i++) {
for (int j=arr.length-1;j>i;j--){
if (arr[j]>arr[j-1]) {
temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
}
return arr;
}
return null;
}
2.选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
// 选择排序
public static int [] getSelectNumber(int [] arr) {
int temp=0;
int index=0;
if(arr!=null && arr.length!=0){
for (int i = 0; i <arr.length-1 ; i++) {
// 记录最大或者最小位置的下标,这样的好处减少了,交换数据的次数
index=i;
for (int j=i+1;j<arr.length;j++){
if (arr[index]<arr[j]) {
index=j;
}
}
if(index!=i){
temp=arr[i];
arr[i]=arr[index];
arr[index]=temp;
}
}
return arr;
}
return null;
}
3、插入排序(Insertion Sort)
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤2~5。
public static int [] insertSort(int [] a){
if(a!=null && a.length!=0) {
int preindex=0;
int current=0;
for(int i=1;i<a.length;i++){
// 当前值之前的index
preindex=i-1;
// 插入当前值
current=a[i];
// 如果当前要插入的current都比前边的值大,就不插入。
// 否则就把比current大的值往后移动,直到current的
// 值比前边的值大或者等于
while (preindex>=0 && a[preindex]>current){
a[preindex+1]=a[preindex];
preindex--;
}
// 如果当前值比前边的值都大,就直接赋值
a[preindex+1]=current;
}
return a;
}
return null;
}
3.4 算法分析
插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
4、希尔排序(Shell Sort)
1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
希尔排序最重要的一点就是分组,在希尔排序中没有明确的说明,通常都是总长度的一半。再分组是上一次分组的一半,知道分组序列为1为止。每个分组进行交换的都是插入交换。
public void int [] xierSort(int [] arr){
for(int midd=arr.length/2;midd>0;midd=midd/2){
for(int i=midd;i<arr.length;i++){
int j=i;
int current=arr[i];
while(j-midd>=0 && current<arr[j-midd]){
arr[j]=arr[j-midd];
j=j-midd;
}
arr[j]=current;
}
}
return arr;
}
4.1 算法描述
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列个数k,对序列进行k 趟排序;
每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
5、归并排序(Merge Sort)
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。分治法是将一个问题分解成一个个小的子问题。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
归并排序是把一个待排序的序列划分成一个个的小的序列,先进行分组,直到分成一个个单独的一个数据。开始比较然后合并分组。
在这里插入代码片package com.banner.rabbitmq;
/**
* @auther liu
* @date 2020/12/16 20:18
*/
public class GuiBingTest {
public static void main(String[] args) {
int[] arr = {2, 8, 92, 3, 4, 23, 89, 9};
mergeSort(arr);
getOut(arr);
}
public static void getOut(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + ",");
}
}
// 归并排序入口
public static void mergeSort(int[] arr) {
// 创建一个临时数组
int[] temp = new int[arr.length];
msSort(arr, temp, 0, arr.length - 1);
}
/**
* 拆分
*
* @param arr
* @param temparr
* @param left
* @param right
*/
public static void msSort(int[] arr, int[] temparr, int left, int right) {
// 证明只有一个元素的数据,本生就是有序的,只需要被归并即可
if (left < right) {
int midd = (left + right) / 2;
// 拆分左边区域
msSort(arr, temparr, left, midd);
// 拆分右边区域
msSort(arr, temparr, midd + 1, right);
// 合并
merge(arr, temparr, left, midd, right);
}
}
private static void merge(int[] arr, int[] temparr, int left, int midd, int right) {
// 标记左半区第一个未排序的下标
int lpos = left;
// 标记右边的第一个区域的第一个区域
int rpos = midd + 1;
// 临时数组元素的下标
int pos = left;
// 合并
while (lpos <= midd && rpos <= right) {
if (arr[lpos] < arr[rpos]) {
// 左边第一个区域值更小
temparr[pos++] = arr[lpos++];
} else {
// 右边的值更小
temparr[pos++] = arr[rpos++];
}
}
//合并最左边的
while (lpos <= midd) {
temparr[pos++] = arr[lpos++];
}
// 合并最右边的
while (rpos <= right) {
temparr[pos++] = arr[rpos++];
}
// 临时数组复制给原来的数组
while (left <= right) {
arr[left] = temparr[left];
left++;
}
}
}
5.1 算法描述
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
6.堆排序
所谓堆排序要明白什么是堆,堆分为两种大根堆,小根堆。大根堆就是任意一个节点的父节点大于它的左右子节点,称之为大根堆,小根堆和这个差不多,只不过是小于左右两个子节点,称之为小根堆。
要对堆排序首先要构建堆。
构建堆的步骤就是首先,要找到左右子节点的最大值然后进行hepfiy就可以。
package com.banner.rabbitmq;
/**
* @auther liu
* @date 2020/12/17 9:53
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {2, 8, 0, 8, 6, 10, 23};
heap_Sort(arr);
//heapify(arr,arr.length,2);
getOut(arr);
}
public static void heap_Sort(int[] arr) {
// 先构建一个堆
buildHeapfiy(arr);
for (int i = arr.length - 1; i >= 0; i--) {
// 每次需要交换第一个和最后一个节点的值
swap(arr, i, 0);
// 砍掉最后一个值
heapify(arr, i, 0);
}
}
public static void buildHeapfiy(int[] arr) {
int lastnode = arr.length - 1;
int parentnode = (lastnode - 1) / 2;
for (int i = parentnode; i >= 0; i--) {
heapify(arr, arr.length, i);
}
}
public static void getOut(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + ",");
}
}
public static void heapify(int[] arr, int n, int i) {
if (i >= n) {
return;
}
int lnode = 2 * i + 1;
int rnode = 2 * i + 2;
int max = i;
// 找到最大值
if (lnode < n && arr[lnode] > arr[max]) {
max = lnode;
}
if (rnode < n && arr[rnode] > arr[max]) {
max = rnode;
}
// 交换父节点的值和最大子节点的值
if (max != i) {
swap(arr, i, max);
// 单次heapify是没有必要递归的,如果是构建heap一定要递归
heapify(arr, n, max);
}
}
public static void swap(int[] arr, int i, int max) {
int temp = arr[i];
arr[i] = arr[max];
arr[max] = temp;
}
}
7.计数排序
计数排序(Count Sort)是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。主要是以空间换时间。
public static void CountSort(int [] arr){
//寻找最大值,最小值
int max=arr[0];
int min=arr[0];
for(int i=0;i<arr.length;i++){
if(arr[i]>max){
max=arr[i];
}
if(arr[i]<min){
min=arr[i];
}
}
// 开辟空间 range
int [] temp=new int [max-min+1];
for (int i = 0; i <arr.length ; i++) {
// 对应值的下标,看有几个相同的值。
temp[arr[i]-min]++;
}
int index = 0;
for(int i = 0;i < temp.length;++i)//遍历辅助空间
{
while((temp[i]--)>0)//下标处的数值是几,说明该数出现了几次
{
arr[index++] = i + min;//将下标处的数对应回原数组
}
}
}
典型的空间换时间。
8.桶排序
桶排序:将待排序的数据分到几个有序的桶里,每个桶的数据单独排序,桶内排完序后,再按顺序依次取出,组成有序序列。
桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。
桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。
步骤:
找出序列中的最大和最小值,目的是为了确定所需桶的数量;
将数据放入相应的桶;
桶内数据进行排序,可以按照快排等算法进行排序;
桶内数据有序取出并合并成完整的有序序列。
public static void bucketSort(int[] arr){
// 计算最大值与最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; i++){
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
// 计算桶的数量
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketArr.add(new ArrayList<Integer>());
}
// 将每个元素放入桶
for(int i = 0; i < arr.length; i++){
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
// 对每个桶进行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
// 将桶中的元素赋值到原序列
int index = 0;
for(int i = 0; i < bucketArr.size(); i++){
for(int j = 0; j < bucketArr.get(i).size(); j++){
arr[index++] = bucketArr.get(i).get(j);
}
}
}
还有两个排序,基数排序和快排,之后会继续更新。