首页 > 编程语言 >[新编]八大经典排序算法的代码实现

[新编]八大经典排序算法的代码实现

时间:2022-09-01 00:24:12浏览次数:56  
标签:index arr 新编 int void 算法 static 排序 复杂度

冒泡排序

 //冒泡排序
 //时间复杂度为O(N^2),空间复杂度为O(N)
 public class BubbleSort {
     public static void bubbleSort(int[] arr) {
         if (arr.length == 0 || arr.length == 1) {
             return;
         } else {
 //            随着每轮比较的进行,都有一个大数沉到后面排好序,因此外层的循环长度应该递减
             for (int end = arr.length - 1; end > 0; end--) {
                 for (int i = 0; i < end; i++) {
                     if (arr[i] > arr[i + 1]) {
                         swap(arr, i, i + 1);
                     }
                 }
             }
         }
 
     }
 
     static void swap(int[] arr, int i, int j) {
 //        不利用第三个变量交换两变量的位置。1.a和同一个数异或运算两次得到a本身 2.异或运算满足交换律
         arr[j] = arr[j] ^ arr[i];
         arr[i] = arr[j] ^ arr[i];
         arr[j] = arr[j] ^ arr[i];
     }
 
     public static void main(String[] args) {
         int[] a = {2, 1, 7, 10, 3, 9, 5, 4, 6, 8};
         bubbleSort(a);
         for(int i:a)
             System.out.print(i+",");
     }
 }

插入排序

//插入排序
//复杂度和数据状况有关系,如果本来数组的有序性就比较好则复杂度低
public class InsertSort {
    public static void insertSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        } else {
            for (int i = 1; i < arr.length; i++) {
//如果数组的有序性比较好,如1,2,3,4,5,则arr[j + 1] < arr[j]这个条件可以使得比较提前终止,
//如果数组刚好是逆序的,如5,4,3,2,1,则需要从j一直比较到i=0;
                for (int j = i - 1; j >= 0 && arr[j + 1] < arr[j]; j--) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }

    static void swap(int[] arr, int i, int j) {
        arr[j] = arr[j] ^ arr[i];
        arr[i] = arr[j] ^ arr[i];
        arr[j] = arr[j] ^ arr[i];
    }

    public static void main(String[] args) {
        int[] a = {2, 1, 7, 10, 3, 9, 5, 4, 6, 8};
        insertSort(a);
        for (int i : a)
            System.out.print(i + ",");
    }
}

选择排序

  //选择排序
  //时间复杂度为O(N^2),空间复杂度为O(1)
  public class SelectionSort {
      public static void selectionSort(int[] arr) {
          if (arr == null || arr.length < 2) {
              return;
          } else {
  //            每轮都从未排序的数列中取出一个数,将其与后面所有未排序的数作比较,得到这些未排序数列里面的最小数,将它换到已排好序数列的后面,并扩大已排好序数列的范围。
             for (int i = 0; i < arr.length - 1; i++) {
                 int minIndex = i;
 //                i = 0作为第一个已排序列
                 for (int j = i + 1; j < arr.length; j++) {
                     minIndex = arr[j] < arr[minIndex] ? j : minIndex;
                 }
                 swap(arr, i, minIndex);
             }
         }
     }
 
     static void swap(int[] arr, int i, int j) {
 //        此处不能用异或来完成交换,因为如果i=j, 两个相同的数异或等于0,“arr[j] = arr[j] ^ arr[i]”会将arr[i]和arr[j]同时置为0,这样就丢失了所有信息。
 //        如果i和j不相等,但a[i]==a[j]是可以完成异或交换功能的,因为0和任何数异或等于其本身
 //        arr[j] = arr[j] ^ arr[i];
 //        arr[i] = arr[j] ^ arr[i];
 //        arr[j] = arr[j] ^ arr[i];
         int tmp = arr[i];
         arr[i] = arr[j];
         arr[j] = tmp;
     }
 
     public static void main(String[] args) {
         int[] a = {2, 1, 7, 10, 3, 9, 5, 4, 6, 8};
         selectionSort(a);
         for (int i : a)
             System.out.print(i + ",");
     }
 }

归并排序

 //归并排序
 //时间复杂度O(NlogN),空间复杂度O(N)
 //分治+外排的方法
 public class MergeSort {
     public static void mergeSort(int[] arr) {
         if (arr == null || arr.length < 2)
             return;
         else
             sortProcess(arr, 0, arr.length - 1);
     }
 
     private static void sortProcess(int[] arr, int L, int R) {
         if (L == R)
             return;
         else {
             int mid = L + ((R - L) >> 1);
 //            根据Master公式求其时间复杂度:
             sortProcess(arr, L, mid);//T(N/2)
             sortProcess(arr, mid + 1, R);//T(N/2)
             merge(arr, L, mid, R);//O(N)
 //            根据Master公式,其时间复杂度为T(N) = 2T(N/2)+O(N) = N*logN
         }
     }
 
     //融合两个有序数组,使之成为一个更大的有序数组的方法,叫做外排
     private static void merge(int[] arr, int l, int mid, int r) {
 //        空间复杂度O(体现在需要一个大小为数据量N的辅助数组help上)
         int[] help = new int[r - l + 1];
         int i = 0;
         int p1 = l;
         int p2 = mid + 1;
         while (p1 <= mid && p2 <= r)
             help[i++] = arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
 //        两个必有且只有一个越界
         while(p1<=mid)
             help[i++] = arr[p1++];
         while(p2<=r)
             help[i++] = arr[p2++];
 
         i = 0;
         while(l<=r)
             arr[l++] = help[i++];
     }
 
     public static void main(String[] args) {
         int[] a = {2, 1, 7, 10, 3, 9, 5, 4, 6, 8};
         mergeSort(a);
         for(int i:a)
             System.out.print(i+",");
     }
 }

快速排序

import java.util.Arrays;

//快排
//时间复杂度最好为O(NlogN). 数组逆序的时候最差,时间复杂度为O(N^2),可以通过随机快排的方式使得其长期时间复杂度期望为O(N*logN)
//空间复杂度最好为O(logN),数组逆序的时候最差,空间复杂度为O(N),额外空间主要是每次partition函数返回的二元数组造成的。
//通过随机快排的方式使得其长期时间复杂度期望为O(NlogN)
//所有递归函数都可以改为非递归版本,因为递归的本质行为是系统在帮我们压栈。改为非递归就是改成我们自己来压栈
// 在工程上是不允许递归行为存在的,因为递归过深可能会导致系统栈爆满,系统不稳定。因此工程上的快排都是非递归版本实现的。
//库函数都是高度优化过的
public class QuickSort {


    static void quickSort(int[] arr, int L, int R) {
        if (L < R) {
//            随机快排, 每次将中间随机一个数和数列最后一个元素交换位置,防止逆序数列产生差的结果
            swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
            int[] p = partition(arr, L, R);
            quickSort(arr, L, p[0] - 1);
            quickSort(arr, p[1] + 1, R);
        }
    }

    //分隔函数,此函数以arr[R]上的元素为标准,把arr上L到R的元素调整成大于arr[R]的都放他左边,小于arr[R]的都放他右边
    //由于数组中可能有多个等于分隔标准的元素,这个函数的返回值时调整完毕时,这若干个等于分隔标准的元素的左边界下标和有边界下标。
    //当只有一个等于分隔标准的元素存在时,左边界 = 有边界 = 该元素下标
    static int[] partition(int[] arr, int L, int R) {
        int less = L - 1;//less表示小于分隔标准arr[R]的元素构成的区域的右边界
        int more = R;//more表示大于分隔标准arr[R]的元素构成的区域的左边界
        int cur = L;
        int base = arr[R];
//        以arr[R]作为基准,有了随机快排,这里的arr[R]被重新洗牌
//        这里一次性处理了大于基准等于基准和小于基准的三种情况,速度比传统快排要快--属于三路快排
        while (cur < more) {
            if (arr[cur] < base) {
                // cur++,因为换到cur位置上的一定是比基准arr[R]小的数,直接将其扩到less范围去,且cur指向下一位置
                swap(arr, ++less, cur++);
            } else if (arr[cur] > base) {
                //交换到cur位置上的数大小位置,交换过去的数一定大于基准arr[R], 故more--,将其扩到more区域, 但cur位置不变
                //因为从--more交换过来的元素大小不确定,还需要判断
                swap(arr, --more, cur);
            } else {
                //当前位置和基准arr[R]相等,不扩到less区域和more区域,放在相等区域
                cur++;
            }
        }
        //最后将基准交换到more区域的下一位置
        swap(arr, more, R);
       // 返回相等区域下标,注意此时more位置上是交换过来的基准值,不用加1
        return new int[]{less + 1, more};
    }

    static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int a[] = {49, 38, 65, 97, 76, 13, 27, 49};
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }
}

堆排序

import java.util.Arrays;
/*
*堆就是用数组实现的二叉树,所有它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。
* 堆分为两种:最大堆和最小堆,两者的差别在于节点的排序方式。

在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这个属性对堆中
的每一个节点都成立。
建堆和调整的过程都要遵循堆的这个属性
* */

//堆排序
//堆是完全二叉树
//二叉树的底层可以用线性的结构来储存,也就是说可以用数组来储存一个二叉树,通过数组中下标的关系来表示这个堆。
//设完全二叉树的一个节点在数组中的下标为i, 可以用简单二叉树来助记: 父节点1的左孩子是3-->2i+1, 右孩子是4-->2i+2;
//则其父节点的下标应该为(i-1)/2,其左孩子节点应该是2*i+1, 其右孩子节点应该为2*i+2
//特例法: 0 的左孩子是1,右孩子是2: 0 = (1-1)/2 = (2-1)/2; 2*0+1 = 0; 2*0+2=2;
public class HeapSort {
    //先建堆(使用heapInsert将数组所有元素排成堆)
    //再输出,每次输出堆顶元素(把堆顶和堆尾交换),再使用heapify调整堆顶元素使之重新符合大根堆
    static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2)
            return;
        else
            //堆插入
            for (int i = 0; i < arr.length; i++)
                heapInsert(arr, i);
        //输出元素
        int heapSize = arr.length;//堆的大小等于数组的长度
        //交换堆顶和最后一个元素
        swap(arr, 0, --heapSize);
        while (heapSize > 0) {
            heapify(arr, 0, heapSize);
            swap(arr, 0, --heapSize);
        }
    }

    //本函数的作用将数组中位置为index的元素插入到堆中正确位置(对大根堆的任意节点,它都小于它的父节点)
    // --index位置的数不能比它的父节点大,如果比父节点大就交换它和父节点的位置
    static void heapInsert(int[] arr, int index) {
        while (arr[(index - 1) / 2] < arr[index]) {//如果index=0, -1/2=0是根节点
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }

    }

    //如果堆中有某个元素变小了(变得比它的字节点小了,因此不符合大根堆的性质了),就要将这个元素下沉以保持大根堆的属性
    //本函数的作用是当index位置上的元素变小了,就要调整它的位置保持大根堆。
    static void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;//在用数组存储的堆中,节点i的左孩子节点是2*i+1, 右节点是2*i+2;
        //这里heapSize是最后一个元素,做堆排的时候,因为是从堆顶交换来的最大值,所以重新heapify要把它排除在外;
        while (left < heapSize) {
            int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[index] > arr[largest] ? index : largest;
            if (largest == index)
                break;
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int a[] = {49, 38, 65, 97, 76, 13, 27, 49};
        heapSort(a);
        System.out.println(Arrays.toString(a));
    }
}

希尔排序

基数排序

标签:index,arr,新编,int,void,算法,static,排序,复杂度
From: https://www.cnblogs.com/greatLong/p/16645050.html

相关文章

  • 算法总结
    1.经典的青蛙跳台阶packagecom.chenghaixiang.fist.P5;/***@author程海翔*@school石家庄铁道大学*/publicclassP5{}classSolution{publicin......
  • Python根据类中属性自定义排序的方法
    如果以创建的对象作为列表中的元素,那么对列表进行排序时可使用sort()函数或sorted()函数,但要注意的是:①当排序对象为列表的时候两者适合的场景不同②sorted()函数会返......
  • 前端也该刷点算法题——双指针解“链表”题也太香了叭!
    双指针解“链表”题也太香了叭!同步双指针1查找链表中倒数第k个节点剑指Offer22.链表中倒数第k个节点思路:假设链表的长度为n,不难得出倒数第k个节点即为整数第n+......
  • 【CV算法基础】ASMS(adaptive scale meanshift)算法理解
        参考1. ASMS算法(adaptivescalemeanshift);2. 基于YOLOv3和ASMS的目标跟踪算法;3.github_asms;完......
  • 【CV算法基础】GIoU算法理解
    几种IoU的理解IoUIOU是用来衡量两个边界框的重叠程度的。 GIoU论文的地址为:https://arxiv.org/abs/1902.09630github代码地址:https://github.com/generalized-iou这......
  • 【ML算法基础】匈牙利算法理解
    前言匈牙利算法是一种在多项式时间内求解任务分配问题的组合优化算法,匈牙利算法(HungarianAlgorithm)与KM算法(Kuhn-MunkresAlgorithm)是做多目标跟踪的小伙伴很容易在论文......
  • 【CV算法基础】FocalLoss理解
     作者提出focalloss的出发点也是希望one-stagedetector可以达到two-stagedetector的准确率,同时不影响原有的速度。既然有了出发点,那么就要找one-stagedetector的准确......
  • 一致性哈希算法 consistent hashing
     在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先......
  • AI智能分析网关包含哪些深度学习算法?如何赋能场景应用?
    AI深度学习技术正在呈现飞速增长的状态,有数据分析预测,到2030年,AI有望实现13万亿美元的市场规模。尤其是伴随着智慧城市、智能交通、工业互联网、生产制造等应用场景对视频......
  • 算法复杂度
    递归普通情况,n只进行加减,多少的n次方,取决于返回几个voidfun(intn){...returnfun(n-1)}上面的就是O(n)voidfun(intn){...returnfun(n......