一、算法和数据结构


一、新手入门

1.选择排序、冒泡排序、插入排序

public class Demo1 {
    public static void main(String[] args) {
    
		int[] arr  = {4,5,5,6,8,9,3,2,1,7};

        /*
         * 选择排序
         * */
        printArray(arr);
        selectSort(arr);
        printArray(arr);

        /*
         * 冒泡排序
         * */
        printArray(arr);
        bubbleSort(arr);
        printArray(arr);

        /*
        * 插入排序
        * */
        printArray(arr);
        insertSort1(arr);
        insertSort2(arr);
        printArray(arr);
        
    }


	//打印数组
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

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


	//选择排序
    public static void selectSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int N = arr.length;
        for (int i = 0; i < N; i++) {
            int minValueIndex = i;
            for (int j = i + 1; j < N; j++) {
                minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex;
            }
            swap(arr, i, minValueIndex);
        }
    }

	//冒泡排序
    public static void bubbleSort(int[] arr){
        if(arr==null || arr.length<2){
            return;
        }
        int N= arr.length;
        for (int end = N-1;end>=0;end--){
            for (int second =1;second<=end;second++){
                if(arr[second-1]>arr[second]){
                    swap(arr,second-1,second);
                }
            }
        }
    }

	//插入排序(基础版)
    public static  void insertSort1(int[] arr){
        if(arr==null || arr.length<2){
            return;
        }
        int N = arr.length;
        for (int end = 1;end<N;end++){
            int newNumIndex = end;
            while (newNumIndex - 1 >= 0 && arr[newNumIndex - 1] > arr[newNumIndex]) {
                swap(arr,newNumIndex-1,newNumIndex);
                newNumIndex--;
            }
        }
    }

	//插入排序(优化版)
    public static  void insertSort2(int[] arr){
        if(arr==null || arr.length<2){
            return;
        }
        int N = arr.length;
        for (int end = 1;end<N;end++){
            for (int pre = end -1;pre >= 0 && arr[pre] > arr[pre+1];pre--){
                swap(arr,pre,pre+1);
            }
        }
    }

}

2.对数器

public class Demo3 {
    public static void main(String[] args) {
        
        /*
        * 对数器:校验当前的排序算法是否正确
        * */
        int maxLen = 5;
        int maxValue = 1000;
        int testTime  = 10000;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = lenRandomValueRandom(maxLen,maxValue);
            int[] tmp = copyArray(arr1);
            selectSort(arr1);
            if(!isSorted(arr1)){
                for (int j = 0; j < tmp.length; j++) {
                    System.out.print(tmp[j]+" ");
                }
                System.out.println();
                System.out.println("选择排序错了");
            }
        }

    }


    //随机生成一个长度maxLen以内、数字maxValue以内的数组 
    public static int[] lenRandomValueRandom(int maxLen,int maxValue){
        int len = (int)(Math.random()*maxLen);
        int[] ans = new int[len];
        for (int i = 0; i < ans.length; i++) {
            ans[i] = (int)(Math.random()*maxValue);
        }
        return ans;
    }
    
    //拷贝一个新数组
    public static int[] copyArray(int[] arr){
        int[] ans  = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            ans[i] = arr[i];
        }
        return ans;
    }

    //验证是否排序
    public static boolean isSorted(int[] arr){
        if(arr.length<2){
            return true;
        }
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(max>arr[i]){
                return false;
            }
            max = Math.max(max,arr[i]);
        }
        return true;
    }
    

    //选择排序
    public static void selectSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int N = arr.length;
        for (int i = 0; i < N; i++) {
            int minValueIndex = i;
            for (int j = i + 1; j < N; j++) {
                minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex;
            }
            swap(arr, i, minValueIndex);
        }
    }
    
	//交换元素
    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[j];
        arr[j] = arr[i];
        arr[i] = tmp;
    }

}

3.二分法

public class Demo4 {
    public static void main(String[] args) {

        /*
         *二分法
         * */
        int[] arr = {1, 3, 6, 8, 8, 9, 16, 20};
        
        //二分法:验证有序数组arr元素是否包含数字num
        System.out.println(find(arr, 8));
        System.out.println(find(arr, 100));

        //二分法:找到有序数组arr元素>=num的最左边元素的下标
        System.out.println(mostLeftNoLessNumIndex(arr, 8));
        System.out.println(mostLeftNoLessNumIndex(arr, 100));

        //二分法:找到有序数组arr元素<=num的最右边元素的下标
        System.out.println(mostRightNoMoreNumIndex(arr, 8));
        System.out.println(mostRightNoMoreNumIndex(arr, 0));

        
        //二分法:arr整体无序且相邻的元素不相等,求局部最小值
        int maxLen = 10;
        int maxValue = 200;
        int testTime = 1000000;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int[] arr2 = randomArray(maxLen, maxValue);
            int ans = oneMInIndex(arr2);
            if (!check(arr2, ans)) {
                printArray(arr2);
                System.out.println(ans);
                break;
            }
        }
        System.out.println("测试结束");
        
    }


    //二分法:验证有序数组arr元素是否包含数字num
    public static boolean find(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return false;
        }
        int L = 0;
        int R = arr.length - 1;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] == num) {
                return true;
            } else if (arr[mid] < num) {
                L = mid + 1;
            } else {
                R = mid - 1;
            }
        }
        return false;
    }

    //二分法:找到有序数组arr元素>=num的最左边元素的下标
    public static int mostLeftNoLessNumIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int L = 0;
        int R = arr.length - 1;
        int ans = -1;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] >= num) {
                ans = mid;
                R = mid - 1;
            } else {
                L = mid + 1;
            }
        }
        return ans;
    }

    //二分法:找到有序数组arr元素<=num的最右边元素的下标
    public static int mostRightNoMoreNumIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int L = 0;
        int R = arr.length - 1;
        int ans = -1;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] <= num) {
                ans = mid;
                L = mid + 1;
            } else {
                R = mid - 1;
            }
        }
        return ans;
    }
    
    
    //生成随机数组,且相邻数不相等
    public static int[] randomArray(int maxLen, int maxValue) {
        int len = (int) (Math.random() * maxLen);
        int[] arr = new int[len];
        if (len > 0) {
            arr[0] = (int) (Math.random() * maxValue);
            for (int i = 1; i < len; i++) {
                do {
                    arr[i] = (int) (Math.random() * maxValue);
                } while (arr[i] == arr[i - 1]);
            }
        }
        return arr;
    }

    //二分法:arr整体无序且相邻的元素不相等,求局部最小值
    public static int oneMInIndex(int[] arr) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int N = arr.length;
        if (N == 1) {
            return 0;
        }
        if (arr[0] < arr[1]) {
            return 0;
        }
        if (arr[N - 1] < arr[N - 2]) {
            return N - 1;
        }

        int L = 0;
        int R = N - 1;
        //至少有三个数
        while (L < R - 1) {
            int mid = (L + R) / 2;
            if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]) {
                return mid;
            } else {
                if (arr[mid] > arr[mid - 1]) {
                    R = mid - 1;
                } else {
                    L = mid + 1;
                }
            }
        }
        return arr[L] < arr[R] ? L : R;
    }

    //校验局部最小值
    public static boolean check(int[] arr, int minIndex) {
        if (arr.length == 0) {
            return minIndex == -1;
        }
        int left = minIndex - 1;
        int right = minIndex + 1;
        boolean leftBigger = left >= 0 ? arr[left] > arr[minIndex] : true;
        boolean rightBigger = right < arr.length ? arr[right] > arr[minIndex] : true;
        return leftBigger && rightBigger;
    }

	//打印数组
    public static void printArray(int[] arr) {
        for (int num : arr) {
            System.out.print(num + " ");
        }
        System.out.println();
    }

}

4.单向链表、双向链表

public class Demo6 {
    public static void main(String[] args) {

        /*
         * 单向链表
         * */
        //构造单向链表
        Node n1 = new Node(1);
        n1.next = new Node(2);
        n1.next.next = new Node(3);
//        while (n1 != null){
//            System.out.print(n1.value+" ");
//            n1 = n1.next;
//        }
//        System.out.println();
        
        //反转单项链表
        Node n2 = reverseLinkedList(n1);
        
        //打印反转的单项链表
        while (n2 != null){
            System.out.print(n2.value+" ");
            n2 = n2.next;
        }
        System.out.println();
        
        
        /*
        * 双向链表
        * */
        
    }
    

    //单向链表反转
    public static Node reverseLinkedList(Node head) {
        Node pre = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    //单向链表类
    public static class Node { 
        public int value;
        public Node next;

        public Node(int data) {
            value = data;
        }        
    }


    //双向链表反转
    public static DoubleNode reverseDoubleList(DoubleNode head){
        DoubleNode pre = null;
        DoubleNode next = null;
        while (head != null){
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head= next;
        }
        return pre;
    }
    
    //双向链表类
    public static class DoubleNode {
        public int value;
        public DoubleNode last;
        public DoubleNode next;

        public DoubleNode(int data) {
            value = data;
        }     
    }

}

5.队列、栈

public class Demo7 {
    public static void main(String[] args) {

        /*
        * 队列:单向链表实现
        * */

        /*
         * 栈:单向链表实现
         * */

    }

    //队列类
    public static class MyQueue<V>{
        private Node<V> head;
        private Node<V> tail;
        private int size;

        public MyQueue(){
            head = null;
            tail = null;
            size = 0;
        }

        public boolean isEmpty(){
            return size == 0;
        }

        public int size(){
            return size;
        }

        //插入
        public void offer(V value){
            Node<V> cur = new Node<>(value);
            if(tail == null){
                head = cur;
                tail = cur;
            }else {
                tail.next = cur;
                tail = cur;
            }
            size++;
        }

        //弹出
        public V poll(){
            V ans = null;
            if(head != null){
                ans = head.value;
                head = head.next;
                size--;
            }
            if(head == null){
                tail = null;
            }
            return ans;
        }

        //查询
        public V peek(){
            V ans = null;
            if(head != null){
                ans = head.value;
            }
            return ans;
        }

    }


    //栈类
    public static class MyStack<V>{
        private Node<V> head;
        private int size;

        public MyStack(){
            head = null;
            size = 0;
        }

        public boolean isEmpty(){
            return size == 0;
        }

        public int size(){
            return size;
        }

        //插入
        public void push(V value){
            Node<V> cur = new Node<>(value);
            if(head == null){
                head = cur;
            }else {
                cur.next = head;
                head = cur;
            }
            size++;
        }

        //弹出
        public V pop(){
            V ans = null;
            if(head != null){
                ans = head.value;
                head = head.next;
                size--;
            }
            return ans;
        }

        //查询
        public V peek(){
            return head != null ? head.value : null;
        }

    }


    //单向链表类
    public static class Node<V> {
        public V value;
        public Node<V> next;

        public Node(V data) {
            value = data;
        }
    }

}

6.位图

public class Demo8 {
    public static void main(String[] args) {

        /*
        * 位图
        * */
        //int a = 170;

        //等效表达
        //System.out.println(a >> 6);
        //System.out.println(a / 64);

        //等效表达
        //System.out.println(a % 64);
        //System.out.println(a & 63);


        //位图测试
        BitMap bitMap = new BitMap(100);
        bitMap.add(64);
        System.out.println(bitMap.contains(64));

        bitMap.delete(64);
        System.out.println(bitMap.contains(64));

    }

    /*
    * 位图类:可以高效存储数据。
    * 一个long(64字节)可以表示0~63,两个long(128字节)可以表示0~63、64~127,对应字节位置写1即可
    * */
    public static class BitMap{
        private long[] bits;

        public BitMap(int max){
            bits = new long[(max + 64) >> 6];
        }

        public void add(int num){
            bits[num >> 6] |= (1L << (num & 63));
        }

        public void delete(int num){
            bits[num >> 6] &= ~(1L << (num & 63));
        }

        public boolean contains(int num){
            return (bits[num >> 6] & (1L << (num & 63))) != 0;
        }

    }
    
}

7.位运算(实现加减乘除)

package com.demo.algorithm.primary;

public class Demo9 {
    public static void main(String[] args) {

        /*
         * 异或 ^  不同为1
         * 与 &  同1为1
         * 带符号右移 >>  右移空出来的位置用符号位补
         * 不带符号右移 >>>  右移空出来的位置用0补
         * */

        /*
         * 用位运算实现 + - * /
         * */
        System.out.println(add(5,2)); //加

        System.out.println(negNum(2)); //取反
        System.out.println(minus(5,2)); //减

        System.out.println(multi(5,2)); //乘

        System.out.println(isNeg(5)); //判断是否为负数
        System.out.println(div(5,2)); //除

    }


    //加
    public static int add(int a, int b) {
        int sum  = a;
        while (b != 0){
            sum = a ^ b; //无进位相加信息
            b = (a & b) << 1; //进位信息-> b -> b'
            a = sum; //a -> a‘
        }
        return sum;
    }

    //取相反数
    public static int negNum(int n){
        return add(~n,1);
    }

    //减
    public static int minus(int a,int b){
        return add(a,negNum(b));
    }

    //乘
    public static int multi(int a,int b){
        int res = 0;
        while (b != 0){
            if((b & 1) != 0){
                res = add(res,a);
            }
            a <<= 1; //带符号左移1位
            b >>>= 1; //不带符号右移1位
        }
        return res;
    }

    //判断是否为负数
    public static boolean isNeg(int n){
        return n < 0;
    }
    
    //除
    public static int div(int a,int b){
        int x = isNeg(a) ? negNum(a) : a;
        int y = isNeg(b) ? negNum(b) : b;
        int res = 0;
        //x / y
        for (int i = 30;i >= 0;i = minus(i,1)){
            if((x >> i) >= y){
                res |= (1 << i);
                x = minus(x,y << i);
            }
        }
        return isNeg(a) ^ isNeg(b) ? negNum(res) : res; //^可以表示不等
    }
    
    //除(考虑极小值)
    public static int divide(int a,int b){
        if(a == Integer.MIN_VALUE && b == Integer.MIN_VALUE){
            return 1;
        } else if (b == Integer.MIN_VALUE) {
            return 0;
        }else if(a == Integer.MIN_VALUE){
            if(b == negNum(1)){
                return Integer.MAX_VALUE;
            }else {
                /*
                * a / b
                * (a + 1) / b == c
                * a - (c * b) == d
                * d / b == e
                * c + e
                * */
                int ans = div(add(a,1),b);
                return add(ans,div(minus(a,multi(ans,b)),b));
            }
        }else {
            return div(a,b);
        }
    }

}

8.比较器

public class Demo10 {

    public static void main(String[] args) {

        /*
        * 比较器:自定义排序
        * */
        Student s1 = new Student("张三", 3, 25);
        Student s2 = new Student("李四", 7, 29);
        Student s3 = new Student("王五", 2, 18);

        //数组
        Student[] students = {s1,s2,s3};
        print(students);
        System.out.println("========");
        Arrays.sort(students,new IdComparator());
        print(students);
        System.out.println("================");

        //集合
        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        print(arrayList);
        System.out.println("========");
        arrayList.sort(new IdComparator());
        print(arrayList);
        System.out.println("================");

        //优先级队列(内部结构是堆)
        PriorityQueue<Student> heap = new PriorityQueue<>(new IdComparator());
        heap.add(s1);
        heap.add(s2);
        heap.add(s3);
        while (!heap.isEmpty()){
            Student s = heap.poll();
            System.out.println(s);
        }

        //TreeSet
        TreeSet<Student> treeSet = new TreeSet<>(new IdComparator());


    }

    //自定义比较器(按id正序排列)
    public static class IdComparator implements Comparator<Student>{

        @Override
        //如果返回负数,第一个参数在前
        //如果返回正数,第二个参数在前
        //如果返回0,认为谁放在前面无所谓
        public int compare(Student o1, Student o2) {
            return o1.id - o2.id;
        }
    }

    //学生类
    public static class Student{
        public String name;
        public int id;
        public int age;

        public Student(String name, int id, int age) {
            this.name = name;
            this.id = id;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", age=" + age +
                    '}';
        }
    }

    //打印数组
    public static <V> void print(V[] vs){
        for (V v : vs){
            System.out.println(v);
        }
    }

    //打印集合
    public static <V> void print(Collection<V> vs){
        for (V v : vs){
            System.out.println(v);
        }
    }

}

9.二叉树

public class Demo11 {
    public static void main(String[] args) {

        /*
        * 二叉树
        * 先序:头左右
        * 中序:左头右
        * 后序:左右头
        * */

    }

    //二叉树类
    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int v){
            value = v;
        }
    }

    //递归序(每个节点走3次)
    public static void f(Node head){
        if(head == null){
            return;
        }
        //1 在此打印节点为头序
        f(head.left);
        //2 在此打印节点为中序
        f(head.right);
        //3 在此打印节点为后序
    }

    //判断二叉树是否结构相同
    public static boolean isSameTree(Node p,Node q){
        if(p == null ^ q == null){
            return false;
        }
        if(p == null && q == null){
            return true;
        }
        return p.value == q.value && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
    }

    //判断是否为镜面树
    public static boolean isSymmetric(Node root){
        return isMirror(root,root);
    }

    public static boolean isMirror(Node h1,Node h2){
        if(h1 == null ^ h2 == null){
            return false;
        }
        if(h1 == null && h2 == null){
            return true;
        }
        return h1.value == h2.value && isMirror(h1.left,h2.right) && isMirror(h1.right,h2.left);
    }

    //返回一棵树的最大深度
    public static int maxDepth(Node root){
        if(root == null){
            return 0;
        }
        return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
    }

    //用先序数组pre[L1...R1]和中序数组in[L2...R2]重建一棵树
    public static Node buildTree(int[] pre,int[] in){
        if(pre == null || in == null || pre.length != in.length){
            return null;
        }
        HashMap<Integer, Integer> valueIndexMap = new HashMap<>();
        for (int i = 0; i < in.length; i++) {
            valueIndexMap.put(in[i],i);
        }
        return g(pre,0,pre.length - 1,in,0,in.length - 1,valueIndexMap);
    }

    public static Node g(int[] pre,int L1,int R1,int[] in,int L2,int R2,
                         HashMap<Integer,Integer> valueIndexMap){
        if(L1 > R1){
            return null;
        }
        Node head = new Node(pre[L1]);
        if(L1 == R1){
            return head;
        }
        int find = valueIndexMap.get(pre[L1]);
        head.left = g(pre,L1 + 1,L1 + find - L2,in,L2,find - 1,valueIndexMap);
        head.right = g(pre,L1 + find - L2 + 1,R1,in,find + 1,R2,valueIndexMap);
        return head;
    }

    //二叉树按层遍历并收集节点
    public List<List<Integer>> levelOrderBottom(Node root){
        List<List<Integer>> ans = new LinkedList<>();
        if(root == null){
            return ans;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            int size = queue.size(); //控制同层节点数量
            List<Integer> curAns = new LinkedList<>();
            for (int i = 0; i < size; i++) {
                Node curNode = queue.poll();
                curAns.add(curNode.value);
                if(curNode.left != null){
                    queue.add(curNode.left);
                }
                if(curNode.right != null){
                    queue.add(curNode.right);
                }
            }
            ans.add(curAns);
        }
        return ans;
    }

    //判断是否为搜索二叉树:中序遍历从小到大(任意节点均为搜索二叉树,且左树的max<x<右树的min)
    public static class Info{
        public boolean isBST;
        public int max;
        public int min;

        public Info(boolean is,int ma,int mi){
            isBST = is;
            max = ma;
            min = mi;
        }
    }

    public static Info process(Node x){
        if (x == null){
            return null;
        }
        Info leftInfo = process(x.left);
        Info rightInfo = process(x.right);
        
        int max = x.value;
        int min = x.value;
        if(leftInfo != null){
            max = Math.max(leftInfo.max,max);
            min = Math.min(leftInfo.min,min);
        }
        if(rightInfo != null){
            max = Math.max(rightInfo.max,max);
            min = Math.min(rightInfo.min,min);
        }
        
//        //方式1
//        boolean isBST = true;
//        if(leftInfo != null && !leftInfo.isBST){
//            isBST = false;
//        }
//        if(rightInfo != null && !rightInfo.isBST){
//            isBST = false;
//        }
//        //left Max < x < right min
//        boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.value);
//        boolean rightMinMoreX =  rightInfo == null ? true : (rightInfo.min > x.value);
//        if(!leftMaxLessX || !rightMinMoreX){
//            isBST = false;
//        }
        
        //方式2
        boolean isBST = false;
        boolean leftIsBST = leftInfo == null ? true : leftInfo.isBST;
        boolean rightIsBST = rightInfo == null ? true : rightInfo.isBST;
        boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.value);
        boolean rightMinMoreX =  rightInfo == null ? true : (rightInfo.min > x.value);
        if(leftIsBST && rightIsBST && leftMaxLessX && rightMinMoreX){
            isBST = true;
        }
        
        return new Info(isBST,max,min);
    }

    //能否组成路径和:存在根节点到叶节点累加值等于指定的某个数
    public static boolean isSum = false;
    public static boolean hasPathSum(Node root,int sum){
        if(root == null){
            return false;
        }
        isSum = false;
        process2(root,0,sum);
        return isSum;
    }
    
    public static void process2(Node x,int preSum,int sum){
        //x是叶节点
        if(x.left == null && x.right == null){
            if(x.value + preSum == sum){
                isSum = true;
            }
        }
        //x是非叶节点
        preSum += x.value;
        if(x.left != null){
            process2(x.left,preSum,sum);
        }
        if(x.right != null){
            process2(x.right,preSum,sum);
        }
    }
    
    //收集达标路径和
    public static List<List<Integer>> pathSum(Node root,int sum){
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null){
            return ans;
        }
        ArrayList<Integer> path = new ArrayList<>();
        process3(root,path,0,sum,ans);
        return ans;
    }
    
    public static void process3(Node x,List<Integer> path,int preSum,int sum,List<List<Integer>> ans){
        if(x.left == null && x.right == null){
            if(preSum + x.value == sum){
                path.add(x.value);
                ans.add(copy(path));
                path.remove(path.size() - 1);
            }
            return;
        }
        //x为非叶节点
        path.add(x.value);
        preSum += x.value;
        if(x.left != null){
            process3(x.left,path,preSum,sum,ans);
        }
        if(x.right != null){
            process3(x.right,path,preSum,sum,ans);
        }
        path.remove(path.size() - 1);
    }
    
    //拷贝集合
    public static <T> List<T> copy(List<T> path){
        ArrayList<T> ans = new ArrayList<>(path);
        return ans;
    }

}

10.归并排序、快速排序

public class Demo13 {
    public static void main(String[] args) {

        /*
         * 归并排序
         * */

        /*
         * 快速排序
         * */
        int[]  arr = {7,1,3,5,4,5,1,4,2,4,2,3};
//        splitNum(arr); //1 3 1 2 2 3 7 4 5 4 4 5 (以原数组最后一位元素为界限,<=在左,>在右)
//        splitNum2(arr); //2 1 2 1 3 3 4 4 4 5 7 5  (以原数组最后一位元素为界限,<在左,=在中,>在右)
        quickSort(arr); //1 1 2 2 3 3 4 4 4 5 5 7 (从小到大排序)
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();

    }

    /*
     * 归并排序
     * */
    //递归实现
    public static void mergeSort1(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        process(arr,0,arr.length-1);
    }
    //arr[L...R]范围上,请让这个范围的数有序
    public static void process(int[] arr,int L,int R){
        if(L == R){
            return;
        }
        //int mid = (L + R) / 2
        int mid = L + ((R - L) >> 1);
        process(arr,L,mid); //保证左边有序
        process(arr,mid + 1,R); //保证右边有序
        merge(arr,L,mid,R); //保证左右一起有序
    }
    public static void merge(int[] arr,int L,int M,int R){
        int[] help = new int[R - L + 1];
        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 (int j = 0; j < help.length; j++) {
            arr[L+j] = help[j];
        }
    }

    /*
     * 快速排序
     * */
    public static void splitNum(int[] arr){
        int lessEqualR = -1;
        int index = 0;
        int N = arr.length;
        while (index < N){
            if(arr[index] <= arr[N - 1]){
                swap(arr,++lessEqualR,index++);
            }else {
                index++;
            }
        }
    }

    public static void splitNum2(int[] arr){
        int N = arr.length;
        int lessR = -1;
        int MoreL = N - 1;
        int index = 0;
        while (index < MoreL){
            if(arr[index] < arr[N -1]){
                swap(arr,++lessR,index++);
            } else if (arr[index] > arr[N - 1]) {
                swap(arr,--MoreL,index);
            }else {
                index++;
            }
        }
        swap(arr,MoreL,N -1);
    }

    //arr[L...R]范围上,拿arr[R]做划分值
    //L...R < = >
    //返回=区间的开始下标和结束下标
    public static int[] partition(int[] arr,int L,int R){
        int lessR = L - 1;
        int moreL = R;
        int index = L;
        while (index < moreL){
            if(arr[index] < arr[R]){
                swap(arr,++lessR,index++);
            } else if (arr[index] > arr[R]) {
                swap(arr,--moreL,index);
            }else {
                index++;
            }
        }
        swap(arr,moreL,R);
        return new int[]{lessR + 1,moreL};
    }
    public static void quickSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        quickSortProcess(arr,0,arr.length - 1);
    }

    public static void quickSortProcess(int[] arr,int L,int R){
        if(L >= R){
            return;
        }
        //L < R
        int[]  equalE = partition(arr,L,R);
        //equal[0]等于区域第一个数
        //equal[1]等于区域最后一个数
        quickSortProcess(arr,L,equalE[0] - 1);
        quickSortProcess(arr,equalE[1] + 1,R);
    }

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

}

二、体系学习

1.复杂度、对数器、二分法

package com.demo.algorithm.middle;

public class Demo1 {
    public static void main(String[] args) {

        /*
        *复杂度
        *
        * 1、常数操作:执行时间固定的操作
        *
        * 2、时间复杂度:最高阶为准,需要保证拆分出来的所有行为都是常数时间操作的
        * O(1)、O(logN)、O(N)、O(N*logN)、O(N^2)、O(N^3)、O(N^k)、O(2^N)、O(3^N)、O(K^N)、O(N!)
        *
        * 3、额外空间复杂度:程序员自主空间,和功能无关
        *
        * 4、最优解:时间复杂度和空间复杂度都最优
        *
        *
        * 对数器
        *
        *
        * 二分法:每次砍一半
        *
        * */

    }

    //插入排序
    public static void insertSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j++) {
                swap(arr,j,j+1);
            }
        }
    }

    //交换数组元素
    public static void swap(int[] arr,int i,int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

}

2.异或运算

public class Demo2 {
    public static void main(String[] args) {

        /*
        * 异或运算:不同为1,相同为0(无进位相加)
        *
        * 0 ^ N = N
        * N ^ N = 0
        * a ^ b = b ^ a                  (满足交换律)
        * (a ^ b) ^ c = a ^ (b ^ c)      (满足结合率)
        * */

        //不用额外变量交换两个数
        int[] arr = {5,8,8};
        swap(arr,0,1);
        System.out.println(arr[0]+","+arr[1]);

        //一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数
        printOddTimesNum(arr);

        //怎么把一个int类型的数,提取出最右侧的1来
        //-a = ~a + 1
        int test = 7;                           //1 1 1
        System.out.println(~test + 1);          //0 0 1
        System.out.println(-test);
        System.out.println(test & (-test));     //0 0 1

        //一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两个数
        arr = new int[]{5,8,8,4};
        printTwoOddTimesNum(arr);

        //一个数组中只有一种数出现K次,其他数都出现了M次,M>1,K<M
        //找到出现K次的数,要求额外空间复杂度O(1)、时间复杂度O(N)
        arr = new int[]{5,5,5,8,8,8,9,9};
        int i = onlyKTimes(arr, 2, 3);
        System.out.println(i);

        //测试
        i = testOnlyKTimes(arr, 2, 3);
        System.out.println(i);

        //对数器测试
        int kinds = 10;
        int range = 200;
        int testTime = 100000;
        int max = 9;
        System.out.println("测试开始");
        for (int j = 0; j < testTime; j++) {
            int a = (int)(Math.random() * max) + 1;    //a 1~9
            int b = (int)(Math.random() * max) + 1;    //b 1~9
            int k = Math.min(a,b);
            int m = Math.max(a,b);
            if(k == m){
                m++;
            }
            int[] array = randomArray(kinds,range,k,m);
            int ans1 = testOnlyKTimes(array,k,m);
            int ans2 = onlyKTimes(array,k,m);
            if(ans1 != ans2){
                System.out.println("出错了");
            }
        }
        System.out.println("测试结束");


    }


    //不用额外变量交换两个数(前提条件:两个数的内存位置不是同一区域)
    public static void swap(int[] arr,int i,int j){
        //异或运算性质的应用
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

    //一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数
    public static void printOddTimesNum(int[] arr){
        //odd表示奇数,even表示偶数
        int  eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        System.out.println(eor);
    }

    //一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两个数
    public static void printTwoOddTimesNum(int[] arr){
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];       //eor = a ^ b
        }
        //a和b是两种数,eor != 0,eor最右侧的1提取出来【数组肯定有的此位为1,有的为0;a、b一个为0,一个为1】
        int rightOne = eor & (-eor);
        int onlyone = 0;
        for (int i = 0; i < arr.length; i++) {
            if((arr[i] & rightOne) != 0){
                onlyone ^= arr[i];
            }
        }
        //onlyone是a、b其中一个
        System.out.println(onlyone+","+(eor ^ onlyone));
    }

    //一个数组中只有一种数出现K次,其他数都出现了M次,M>1,K<M
    //找到出现K次的数,要求额外空间复杂度O(1)、时间复杂度O(N)
    public static int onlyKTimes(int[] arr,int k,int m){
        int[] t = new int[32];
        //t[i] i位置的1出现了几个
        for (int num : arr){
            for (int i = 0; i < t.length; i++) {
                t[i] += (num >> i) & 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < t.length; i++) {
            if(t[i] % m == k){  //第i位上有1
                ans |= (1 << i);
            }
        }
        return ans;
    }

    //测试
    public static int testOnlyKTimes(int[] arr,int k,int m){
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : arr){
            if(map.containsKey(num)){
                map.put(num,map.get(num) + 1);
            }else {
                map.put(num,1);
            }
        }
        for (int num : map.keySet()){
            if(map.get(num) == k){
                return num;
            }
        }
        return -1;
    }

    //随机生成数组:数字最大种数kinds,取值范围-range~range,只有一种数出现K次,其他数都出现了M次,M>1,K<M
    public static int[] randomArray(int maxKinds,int range,int k,int m){
        //数字种类数
        int numKinds = (int)(Math.random() * maxKinds) + 2;
        int randomNum = randomNumber(range);
        //数组长度 = k * 1 + m * (numKinds - 1)
        int[] arr = new int[k + m * (numKinds - 1)];
        int index = 0;
        for (;index < k;index++){
            arr[index] = randomNum;
        }
        numKinds--;
        HashSet<Integer> set = new HashSet<>();
        set.add(randomNum);
        while (numKinds != 0){
            do {
                randomNum = randomNumber(range);
            }while (set.contains(randomNum));
            set.add(randomNum);
            numKinds--;
            for (int i = 0; i < m; i++) {
                arr[index++] = randomNum;
            }
        }
        //arr填好了
        for (int i = 0; i < arr.length; i++) {
            //i位置的数随机和j位置的数交换
            int j = (int)(Math.random() * arr.length);  //0~N-1
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        return arr;
    }

    //随机生成一个-range~range之间的整数
    public static int randomNumber(int range){
        return ((int)(Math.random() * range) + 1) - ((int)(Math.random() * range + 1));
    }
    
}

3.基础数据结构

public class Demo3 {
    public static void main(String[] args) {

        //哈希表,HashMap
        HashMap<Object, Object> hashMap = new HashMap<>();

        //有序表,TreeMap
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(1, "我是1");
        treeMap.put(4,"我是4");
        treeMap.put(2,"我是2");
        treeMap.put(3,"我是3");

        System.out.println(treeMap.containsKey(1));
        System.out.println(treeMap.containsKey(10));

        System.out.println(treeMap.get(4));
        treeMap.put(4,"他是4");
        System.out.println(treeMap.get(4));

        System.out.println(treeMap.firstKey());
        System.out.println(treeMap.lastKey());

        //<=3
        System.out.println(treeMap.floorKey(3));
        //>=2
        System.out.println(treeMap.ceilingKey(2));


    }


    //单向链表类
    public static class Node{
        public int value;
        public Node next;

        public Node(int data){
            value = data;
        }
    }

    //单项链表反转
    public static Node reverseLinkedList(Node head){
        Node pre = null;
        Node next = null;
        while (head != null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    //单项链表删除某个数
    public static Node removeValue(Node head,int num){
        //haed来到第一个不需要删的位置
        while (head != null){
            if(head.value != num){
                break;
            }
            head = head.next;
        }

        // 1)head == null(直接返回head)
        // 2)head != null (等于num的跳过,不等于num的连上)
        Node pre = head;
        Node cur = head;
        while (cur != null){
            if(cur.value == num){
                pre.next = cur.next;
            }else {
                pre = cur;
            }
            cur = cur.next;
        }

        return head;
    }


    //双向链表


    //队列类
    public static class Queue{
        private int[] arr;
        private int pushi; //end
        private int pooli; //begin
        private int size;
        private final int limit;

        public Queue(int limit){
            arr = new int[limit];
            pushi = 0;
            pooli = 0;
            size = 0;
            this.limit = limit;
        }

        public void push(int value){
            if(size == limit){
                throw new RuntimeException("队列满了,不能再加了");
            }
            size++;
            arr[pushi] = value;
            pushi = nextIndex(pushi);
        }

        public int pop(){
            if(size == 0){
                throw new RuntimeException("队列空了,不能再拿了");
            }
            size--;
            int ans = arr[pooli];
            return ans;
        }

        public boolean isEmpty(){
            return size == 0;
        }

        //如果现在的下标是i,返回下一个位置
        private int nextIndex(int i){
            return i < limit - 1 ? i + 1 : 0;
        }

    }

    //队列(用两个栈实现,单向倒腾、1推1弹)
    public static class TwoStackImplementQueue{
        public Stack<Integer> stackPush;
        public Stack<Integer> stackPop;

        public TwoStackImplementQueue(){
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }

        //push栈向pop栈导入数据
        private void pushToPop(){
            if(stackPop.empty()){
                while (!stackPush.empty()){
                    stackPop.push(stackPush.pop());
                }
            }
        }

        public void add(int pushInt){
            stackPush.push(pushInt);
            pushToPop();
        }

        public int poll(){
            if(stackPop.empty() && stackPush.empty()){
                throw new RuntimeException("Queue is empty!");
            }
            pushToPop();
            return stackPop.pop();
        }

        public int peek(){
            if(stackPop.empty() && stackPush.empty()){
                throw new RuntimeException("Queue is empty");
            }
            pushToPop();
            return stackPop.peek();
        }

    }


    //栈(用两个队列实现,轮流倒腾)
    public static class TwoQueueImplementStack<T>{
        public java.util.Queue<T> queue;
        public java.util.Queue<T> help;

        public TwoQueueImplementStack(){
            queue = new LinkedList<>();
            help = new LinkedList<>();
        }

        public void push(T value){
            queue.offer(value);
        }

        public T poll(){
            while (queue.size() > 1){
                help.offer(queue.poll());
            }
            T ans = queue.poll();
            java.util.Queue<T> tmp = queue;
            queue = help;
            help = tmp;
            return ans;
        }

        public T peek(){
            while (queue.size() > 1){
                help.offer(queue.poll());
            }
            T ans = queue.poll();
            help.offer(ans);
            java.util.Queue<T> tmp = queue;
            queue = help;
            help = tmp;
            return ans;
        }

        public boolean isEmpty(){
            return queue.isEmpty();
        }

    }


    //递归(大化小)
    //求arr中的最大值
    public static int getMax(int[] arr){
        return process(arr,0, arr.length - 1);
    }

    //arr[L...R]范围上求最大值
    public static int process(int[] arr,int L,int R){
        //arr[L...R]范围上只有一个数,直接返回
        if(L == R){
            return arr[L];
        }
        //L...R不只有一个数
        //mid = (L + R) / 2
        int mid = L + ((R-L) >> 1);//中点
        int leftMax = process(arr,L,mid);
        int rightMax = process(arr,mid + 1,R);
        return Math.max(leftMax,rightMax);
    }


    //Master公式(确定时间复杂度)
    /*
    * 形如
    * T(N) = a * T(N/b)+ O(N^d)(其中a、b、c都是常数)
    * 的递归函数,可以直接通过Master公式来确定时间复杂度
    * 如果log(b,a) < d,复杂度为O(N^d)
    * 如果log(b,a) > d,复杂度为O(N^log(b,a))
    * 如果log(b,a) == d,复杂度为O(N^d * logN)
    * */


}

4.

5.

6.

7.

三、大厂试题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

架构之火

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值