文章目录
0.算法复杂度比较
1.直接插入排序
插入排序(Insertion Sort) 是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
class Insert {
public static int[] insert(int[] arr) {
int length = arr.length;
for (int i=0; i<arr.length; i++) {
int tmp = arr[i];
int j;//j从当前的i开始向前遍历
for (j=i; j>0 && arr[j-1]>tmp; j--) {
//i是当前值,如果arr[j-1]比当前值大,arr[j-1]就要向后移一位。
arr[j] = arr[j-1];
}
arr[j] = tmp;//当前的j就是应该插入的位置;
}
return arr;
}
public static void main(String[] args){
int[] arr = new int[]{
3,6,8,4,5,7,11};
arr = insert(arr);
System.out.println(Arrays.toString(arr));
}
}
参考:
https://blog.csdn.net/qq_28081081/article/details/80594386
2.Shell排序
希尔排序(Shell Sort) 也叫做缩小增量排序,是插入排序的改进版本。 它通过先设置一个增量n,大小为数组长度的一半,将间隔为n的元素视作一个组,然后对每个组内部的元素进行从小到大进行插入排序;然后再将增量n缩小一半,再次进行分组插入排序,直到增量n为1,因为增量为1的时候,所有的元素都为同一个组了。
class Shell{
public static int[] shellSort(int[] arr){
// 1. 获取数组长度
int length = arr.length;
// 2.获取初始的间隔长度
int interval = (int)Math.floor(length/2.0);
// 3. 不断地缩小间隔的大小,进行分组插入排序
while (interval>=1) {
// 4. 从 arr[interval] 开始往后遍历,将遍历到的数据与其小组进行插入排序
for (int i=interval;i<length;i++) {
int tmp = arr[i];//tmp后值
int j = i;
while (j-interval>=0 && arr[j-interval]>tmp){
//前值比后值大的情况
arr[j] = arr[j-interval];//后值用前值代替
j = j-interval;
}
arr[j] = tmp;
}
// 5. 缩小间隔
interval = (int)Math.floor(interval/2.0);
}
return arr;
}
public static void main(String[] args){
int[] arr = new int[]{
3,6,8,4,5,7,11,3,3,3,3,2,1};
arr = shellSort(arr);
System.out.println(Arrays.toString(arr));
}
}
3.直接选择排序
选择排序(Selection sort) 是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,所以称为选择排序。
设第一个元素为比较元素,依次和后面的元素比较,比较完所有元素找到最小的元素,将它和第一个元素互换。重复上述操作,我们找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序。
class Select {
public static int[] select_sort(int[] arr) {
//第一层for表示循环选择的遍数
int length = arr.length;
for (int i=0; i<length-1; i++) {
int iMin = i;//将起始元素设为最小元素
// 第二层for表示最小元素和后面的元素逐个比较
for (int j=i+1; j<length; j++) {
if (arr[j] < arr[iMin]){
//如果当前元素比最小元素小,则把当前元素角标记为最小元素角标
iMin = j;
}
}
// 查找一遍后将最小元素与起始元素互换
if (iMin!=i){
int tmp = arr[iMin];
arr[iMin] = arr[i];
arr[i] = tmp;
}
}
return arr;
}
public static void main(String[] args){
int[] arr = new int[]{
3,6,8,4,5,7,11};
arr = select_sort(arr);
System.out.println(Arrays.toString(arr));
}
}
参考:https://www.cnblogs.com/pythonbao/p/10787340.html
4.堆排序
堆排序(Heap Sort) 的基本思想是: 1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
class Heap {
public static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void heapSort(int[] arr){
int length = arr.length;
// 1.构建大顶堆数组
buildMaxHeap(arr,length);
// 2.交换堆顶和当前末尾的节点,重置大顶堆
for (int i=length-1; i>0; i--) {
swap(arr,0,i);
length--;
heapify(arr,0,length);
}
return;
}
public static void buildMaxHeap(int[] arr,int length) {
int first = (int)Math.floor(length/2.0)-1;//最后一个非叶子结点开始向前遍历
for (int i=first; i>=0; i--) {
heapify(arr,i,length);
}
return;
}
public static void heapify(int[] arr, int i, int length) {
// 先根据堆性质,找出它左右节点的索引
int left = 2 * i + 1;
int right = 2 * i + 2;
// 默认当前节点(父节点)是最大值。
int largestIndex = i;
if (left<length && arr[left]>arr[largestIndex]){
largestIndex = left;
}
if (right<length && arr[right]>arr[largestIndex]){
largestIndex = right;
}
if (largestIndex != i){
//需要交换arr[i]和arr[largestIndex]
swap(arr,i,largestIndex);
//因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
heapify(arr, largestIndex, length);
}
}
public static void main(String[] args){
int[] arr = new int[]{
7,7,6,5,4,3,1,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}
参考:https://blog.csdn.net/qq_28063811/article/details/93034625
5.冒泡排序
冒泡排序(Bubble Sort)
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2.针对所有的元素重复以上的步骤,除了最后一个。
3.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
class Bubble{
public static int[] bubble(int[] arr){
int length = arr.length;
// 第一层for表示循环选择的遍数
for (int i=0; i<length-1; i++){
for (int j=0; j<length-1-i; j++){
if (arr[j] > arr[j+1]){
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
return arr;
}
public static void main(String[] args){
int[] arr = new int[]{
3,6,8,4,5,7,11};
arr = bubble(arr);
System.out.println(Arrays.toString(arr));
}
}
参考: https://www.cnblogs.com/king-ding/p/bubblesort.html
6.快速排序
6.1基本快排
快速排序(Quick Sort) 通过一趟排序将序列分成左右两部分,其中左半部分的的值均比右半部分的值小,然后再分别对左右部分的记录进行排序,直到整个序列有序。
基本快排以首个元素为基准点,设置快慢指针,遇到快指针的值小于基准点的时候,将其与慢指针的值互换位置。最后慢指针的位置最后一个小于temp的元素,将其和start位置的元素(即tmp)互换。
class Quick1 {
public static void quickSort(int[] arr, int start, int end){
if (start<end){
//递归调用
int index = basicSort(arr, start, end);
quickSort(arr, start, index-1);
quickSort(arr, index+1, end);
}
return;
}
public static int basicSort(int[] arr, int start, int end){
int tmp = arr[start];
int j = start; //慢指针从start开始
for (int i=start+1; i<end+1; i++){
//快指针从start+1开始
if (arr[i]<tmp){
//将比tmp小的尽可能往前移动,即如果arr[i]比tmp小,arr[i]就和arr[j+1]互换位置
int t = arr[j+1];
arr[j+1] = arr[i];
arr[i] = t;
j++;
}
}//最后j指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换
int t2 = arr[j];
arr[j] = arr[start];
arr[start] = t2;
return j;
}
public static void main(String[] args){
int[] arr = new int[]{
3,3,8,4,5,7,11};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
参考:https://blog.csdn.net/weixin_37275456/article/details/103348299
6.2二路快排
二路快排和基本快排只有从左向右的指针不同,它使用左右两个指针分别指向start+1 和end ,让数组arr左面的值尽可能小于基准点(首个元素)tmp,右面的值尽可能大于tmp。如果遇到arr[left]>=tmp并且arr[right]<=tmp,那么交换二者的值,然后继续移动,直到右指针小于左指针。最后right指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换。
class Quick2 {
public static void quickSort2(int[] arr, int start, int end){
if (start<end){
int index = basicSort(arr, start, end);
quickSort2(arr, start, index-1);
quickSort2(arr, index+1, end);
}
return;
}
public static int basicSort(int[] arr, int start, int end){
int tmp = arr[start];
int left = start + 1;//左指针
int right = end;//右指针
while (true){
//让arr左面的值尽可能小于tmp 右面的值尽可能大于tmp
while (left<=end && arr[left]<tmp){
left++;
}
while (right>=start+1 && arr[right]>tmp){
right--;
}
//退出循环条件
if (left>right) {
break;
}
//此时说明arr[left]>=tmp并且arr{right]<=tmp,那么交换二者的值
int t = arr[left];
arr[left] = arr[right];
arr[right] = t;
//然后指针开始移动
left++;
right--;
}//最后right指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换
int t2 = arr[right];
arr[right] = arr[start];
arr[start] = t2;
return right;
}
public static void main(String[] args){
int[] arr = new int[]{
3,3,8,4,5,7,11};
quickSort2(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
参考:https://blog.csdn.net/weixin_37275456/article/details/103348299
6.3三路快排
以首个元素为基准点,将元素分为小于 v ,等于 v , 大于 v 三个部分。而元素i则指向当前进行比较的元素
设三个区间:区间[l+1,lt]是小于v的元素,区间[lt+1,i-1]则表示的是等于v的元素,从最右边的索引r处开始往内形成的区间[gt,r]存放的是大于v的元素。
i的开始索引指向l+1,lt的初始值是l,而gt的初始值则是r+1。
当排序开始时 如果当前i 指向的元素 等于v,i+1; 如果当前i 指向的元素 小于v,那么就将 lt+1 与索引 i处的值进行交换, 然后lt+1, 并且 i+1; 如果当前元素 大于 v,那么 就将 gt - 1 处的元素与 当前元素 交换,然后gt-1.
最后当i 走到 gt 处 即 gt==i 时 ;那就说明 除了第一个元素之外,其余的区间已经分区完毕,只要将首个元素与 lt处的元素进行交换, 然后lt -1 ;我们就形成了想要的三个区间,小于v,等于v,然后是大于v的.
class Quick3 {
public static void quickSort3(int[] arr, int start, int end){
if (start<end){
Map<String,Integer> mp = basicSort(arr, start, end);
quickSort3(arr, start, mp.get("left"));
quickSort3(arr, mp.get("right"), end);
}
return;
}
public static Map<String,Integer> basicSort(int[] arr, int start, int end){
//区间 [start+1,left]是小于tmp的元素,区间[left+1,i-1]则表示的是等于tmp的元素,从最右边的索引r处开始往内,形成的区间[right,end]存放的是大于tmp的元素;
int tmp = arr[start];
int left = start; //指针1
int right = end+1; //指针2
for (int i=left+1; i<right; ){
//i是指针3
if (arr[i]==tmp){
//如果当前i指向的元素 等于tmp,那很好,i+1;
i++;
}else if(arr[i]>tmp){
//如果当前元素大于tmp,那么就将right-1处的元素与当前元素 交换,然后gt-1.
int t = arr[i];
arr[i] = arr[right-1];
arr[right-1] = t;
right--;
}else{
//如果当前i 指向的元素 小于tmp,那么就将 left+1 与索引 i处的值 进行交换,然后lt+1, 并且 i+1;
int t2 = arr[i];
arr[i] = arr[left+1];
arr[left+1] = t2;
left++;
i++;
}
}
int t3 = arr[start];
arr[start] = arr[left];
arr[left] = t3;
// left--;
Map<String,Integer> mp = new HashMap<>();
mp.put("left",left);
mp.put("right",right);
System.out.println(mp);
return mp;
}
public static void main(String[] args){
int[] arr = new int[]{
7,6,5,4,3,2,1};
quickSort3(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
参考:https://www.jianshu.com/p/c61d1e82c44b
7.归并排序
归并排序(Merge Sort) 是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
class Merge{
public static int[] divide(int[] arr){
if (arr.length<=1) {
//继续分不下去了
return arr;
}
int length = arr.length;
int mid = (int)Math.floor(length/2.0);
return merge(divide(Arrays.copyOfRange(arr,0,mid)),Arrays.copyOfRange(arr,mid,arr.length));
}
public static int[] merge(int[]x, int[] y){
List<Integer> re = new ArrayList<Integer>();
int px = 0, py = 0;
while (px<x.length && py<y.length){
if (x[px]>y[py]){
re.add(y[py]);
py++;
}else if(x[px]<y[py]){
re.add(x[px]);
px++;
}else{
re.add(x[px]);
re.add(y[py]);
px++;
py++;
}
}
while (px<x.length) {
re.add(x[px]);
px++;
}
while (py<y.length) {
re.add(y[py]);
py++;
}
int[] res = re.stream().mapToInt(Integer::valueOf).toArray();
return res;
}
public static void main(String[] args){
int[] arr = new int[]{
3,6,8,4,5,7,11};
arr = divide(arr);
System.out.println(Arrays.toString(arr));
}
}
8.计数排序
计数排序不是一个比较排序算法,该算法于1954年由 Harold H. Seward提出,通过计数将时间复杂度降到了O(N)
第一步:找出原数组中元素值最大的,记为max。
第二步:创建一个新数组count,其长度是max加1,其元素默认值都为0。
第三步:遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
第四步:创建结果数组result,起始索引index。
第五步:遍历count数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素。
第六步:返回结果数组result
class Bucket {
public static int[] bucket(int[] arr) {
int length = arr.length;
int size = Collections.max(Arrays.stream(arr).boxed().collect(Collectors.toList()));//获得最大值
int[] result = new int[length];
int[] bucket = new int[size+1];//长度为数组的最大值加一
for (int i=0; i<arr.length; i++){
//统计每个元素的出现次数
bucket[arr[i]]++;
}
int j = 0;
for (int i=0; i<bucket.length; i++){
//根据每个元素出现次数得到结果
int tmp = bucket[i];//tmp是出现次数,i是元素的值
while (tmp>0){
result[j] = i;
j++;
tmp--;
}
}
return result;
}
public static void main(String[] args){
int[] arr = new int[]{
3,3,8,4,5,7,11};
arr = bucket(arr);
System.out.println(Arrays.toString(arr));
}
}