排序算法笔记

以下代码均整理自互联网。

快排:// 见笔记本 10页快排的思路
public class Code_04_QuickSort {

    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    public static void quickSort(int[] arr, int l, int r) {
        if (l < r) {
            //随机选一个位置的数与最后位置交换,形成随机快排。
            //  l + (int) (Math.random() * (r - l + 1)) 表示在l和r之间随机选一个数
            swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            //不要上一行就是经典快排
            int[] p = partition(arr, l, r); //partiton划分的意思,这个是荷兰国旗问题
            quickSort(arr, l, p[0]);
            quickSort(arr, p[1], r);
            //进来一组数据,分成左边一坨小于它,右边一坨大于它,然后对左边一坨,中间等于的一坨
            //左边分出的一坨又重新这样做(因为同样的问题,所以调用自己),右边分出来的一坨也这样做,
            // 所以递归多了后面那两个quikSort
            //后面再本程序开头加上递归终止条件即可。归并排序也是这样的,只不过后面多了归并的部分。
        }
    }

    public static int[] partition(int[] arr, int l, int r) {
        int less = l - 1;
        int more = r;//因为是用最后一个数作为基准数的,所以这里是r,后面记得把这个基准数
            // 放到中间相等的部分,且more是中间相等部分的上一个数,自己可以找两个数想一想。
        while (l < more) {   //因为l有时候找到比他大的数是不走的,所以只能用while。
            if (arr[l] < arr[r]) {  //l后续不会交换,所以用来做遍历下标
                swap(arr, ++less, l++); //这里一定要小心
            } else if (arr[l] > arr[r]) {
                swap(arr, --more, l);//这里一定要小心,l是不动的
            } else {
                l++;
            }
        }
        swap(arr, more, r); //最后一位还是与中间相等的数,所以要交换到中间去。
        //more就是相等数的最后一位,但是less是中间相等数的前一位,下一步返回边界要小心
        return new int[] { less, more+1 };  //返回中间相等的上下边界,返回回去后面直接对左右部分同样处理。
    }

    public 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) {
        System.out.println("hello");
        int [] arr = new int [] {5,6,8,31,2};
        quikSort(arr,0,arr.length-1);
        for (int i = 0; i<arr.length; i++) {
            System.out.print(arr[i]);
            System.out.print(" ");
        }
    }

}
插入排序:笔记本第4public class insertSort {
    public static void insertSort(int[] arr) {
        if(arr.length<2||arr==null) return ;
        for(int i = 1; i<arr.length; i++) {
            for(int j = i-1; j>-1 && arr[j+1]<arr[j]; j--)//关键的地方在于这儿
//              核心思路和打牌一样,当前元素和前一个比,小就交换,交换后还要比较,所以j和j+1比,这里是关键
                swap(arr,j+1,j);

        }
    }
    public 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[] p = new int [] {1,5,9,3,66,8,4,3};
        insertSort(p);
        for(int i = 0; i<p.length;i++) {
            System.out.print(p[i]);
            System.out.print(" ");
        }
    }
}
归并排序:笔记本第6页
import java.util.Arrays;
public class mergeSort {
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    public static void mergeSort(int[] arr, int l, int r) {
        if (l == r) {
            return;
        }
        int mid = l + ((r - l) >> 1);//l,r中点的位置,这样写是为了防止溢出。
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);  //左右两边排好序
        merge(arr, l, mid, r);    //对进来的数组进行前后半部分进行重组 
        // 这一步必须写在最后,因为从底部开始合并,然后合并上一步,即对数组的两半部分进行类似树的后续遍历一样
        // 这样做的原因是,本层进行合并的时候,在merge前是要保证左半部分和右半部分是排好序的,所以是后续遍历
        // 这一步也是剑指offer有道题是将两个排序好的链表合并为一个链表。


        // 和刚刚快排很类似,进来一个数组,切成左边一组,右边一组,只要还可以切,
        // 就继续切(所以有了后两步mergesort),直到不能切返回,后面多了merge步骤。
    }  

    public static void merge(int[] arr, int l, int m, int r) {
        int[] help = new int[r - l + 1];//如{3,2,4},l是0,r是2进来,长度是2-0+1=3,所以长度要分好
        int i = 0;
        int p1 = l;
        int p2 = m + 1; //用两个指针去扫描数组的前后部分
        while (p1 <= m && p2 <= r) {
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        // 下面两个循环表示谁走完后,另一个数组直接复制后面的内容。
        while (p1 <= m) {   
        // 这里和下面的循环自己写的时候没写等号,错了想了很久。因为上面截止后,还会自加,所以会超出原边界,依次作为判断条件。
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        // 复制回去
        for (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];   //一定注意下标要对应原始数组的下标
        }
    }
    public static void main(String[] args) {
        int[] p = new int [] {1,5,9,3,66,8,4,3};
        mergeSort(p);
        for(int i = 0; i<p.length;i++) {
            System.out.print(p[i]);
            System.out.print(" ");
        }
    }
}
冒泡排序:
public class maopao {
    public static void bubbleSort(int[] arr) {
        if(arr==null||arr.length<2) return ;
        for(int i = 0; i<arr.length;i++) {
            //每一趟将最大的数放到最后,且下一趟会比上一趟少一个数比较(其实最后一趟没有必要,因为就他一个人)
            for(int j = 0;j<arr.length-1-i;j++) {
                if (arr[j]>arr[j+1])
                    swap(arr,j+1,j);
            }
        }
    }
    public 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[] p = new int [] {1,5,9,3,66,8,4,3};
        bubbleSort(p);//bubble冒泡
        for(int i = 0; i<p.length;i++) {
            System.out.print(p[i]);
            System.out.print(" ");
        }
    }

}
选择排序:
import java.util.Arrays;

public class Code_02_SelectionSort {

    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length - 1; i++) {  //因为j=i+1
            int minIndex = i;
            // 从0位置开始,每一趟找一个最小的数,放在该趟对应的下标处
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] < arr[minIndex] ? j : minIndex; 
                 //记录最小位置的下标,这样减少交换次数。
            }
            swap(arr, i, minIndex);  //这一趟走完,才把这一趟对应位置的数换成后半部分最小的数。
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
        public 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[] p = new int [] {1,5,9,3,66,8,4,3};
        selectionSort(p);//bubble冒泡
        for(int i = 0; i<p.length;i++) {
            System.out.print(p[i]);
            System.out.print(" ");
        }
    }
}
堆排序:笔记本11页
import java.util.Arrays;

public class Code_03_HeapSort {
    // 将数组排序转换为二叉树形式,然后通过大根堆调整得到数组的排序
    public static void heapSort(int[] arr) {
        //第一步判断数组
        if (arr == null || arr.length < 2) {
            return;
        }
        //第二步建立大根堆:heapInsert
            // 遍历每一个节点,如果比父节点大,则交换--->建立大根堆
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        // 第三步:大根堆只保证父比子大,无法保证左右,所以通过不断的让父节点去堆尾节点,然后调整实现排序
        int size = arr.length;
        swap(arr, 0, --size);  //头尾交换 (不可以和while合并写循环第一条?)
        while (size > 0) {
            heapify(arr, 0, size);   //调 大根堆 
            swap(arr, 0, --size);    // 头尾交换,并尾部往前移
        }
    }

    public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {  //若比父节点大则交换,(index - 1) / 2当前节点的父节点
            // 和插入一样,交换后也许还是大,所以要一直交换,知道不用交换为止
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
    // 每次将大根堆放到尾部后,头结点是比较小的,然后头结点和子节点的交换,一直交换到上一步最尾部的上一个数
    public static void heapify(int[] arr, int index, int size) {
        int left = index * 2 + 1;
        while (left < size) {   //看是否越界在叶子节点上
            // left + 1 就是右孩子,下一句是右孩子不越界,且三目运算返回最大孩子的下标
            // 若右节点越界则返回左结点的值(即没有右节点)
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            // 这一句是我和孩子谁大(上一步找到最大的孩子下标),谁大下标给largest
            largest = arr[largest] > arr[index] ? largest : index;
            // if条件表示,节点变小后但仍然比孩子都大,则不用往下沉
            if (largest == index) {   
                break;
            }
            swap(arr, largest, index);
            index = largest;  //头结点变大后,交换的节点还要继续往下沉,让头结点=index
            left = index * 2 + 1;
        }
    }

    public 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[] p = new int [] {1,5,9,3,66,8,4,3};
        heapSort(p);//bubble冒泡
        for(int i = 0; i<p.length;i++) {
            System.out.print(p[i]);
            System.out.print(" ");
    }
}
二分查找特别重要,且其他题目中出现排序的数组就要想下是不是可以用二分查找。

public class erfenchazhao {
    public int solve(int[] arr,int target) {
        if(arr==null) return -1;
        if(arr.length==1&&arr[0] == target) return target;
        if(arr.length==1) return -1;
        if(target>arr[arr.length-1]||target<arr[0]) return -1;
        //上面都是为容错而定义的
        return erfen(arr,0,arr.length-1,target);
    }
    public int erfen(int[] arr,int l,int r,int target) {
        while(l<=r) {  
            //这里必须有等号,因为mid+1或者-1传递给下一次的l,r时
            //l,r指的是同一个值,没有等号的话就无法进入,导致查找失败。
            //注意二分查找一定针对的是有序数组,且看到有序数组就想下是不是可以使用二分查找来做。
            int mid = l+(r-l)/2; //如果两个数很大的话,两者相加会溢出变成负数,这样就都是负数了
            if(arr[mid]>target) 
                r = mid -1;
            else if(arr[mid]<target)
                l = mid +1;
            else return arr[mid];
        }
        return -1;
    }
    public static void main(String[] args) {
        erfenchazhao aa = new erfenchazhao();
        int [] arr = {1,5,6,9,10,16,28,37};
        System.out.println(aa.solve(arr,6));
    }
}

整理于:
https://mp.weixin.qq.com/s?__biz=MzIwNTc4NTEwOQ==&mid=2247485073&idx=2&sn=367c47435e3a840c74ce71f7be041cde&scene=21#wechat_redirect 

面试常考的常用数据结构与算法:https://blog.csdn.net/zwj1030711290/article/details/70224852

https://www.cnblogs.com/onepixel/articles/7674659.html 排序算法这个上面总结的很好。
排序算法的一点点应用-海量数据处理,这个总结的好:
https://blog.csdn.net/v_JULY_v/article/details/7382693

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值