算法

400 阅读5分钟

排序

冒泡

越小的元素会经由交换 慢慢 “浮” 到数列的顶端

以升序排列为例:相邻元素两两比较,每次选到最大的放到最后

时间复杂度:若本身是有序的,n-1一次,时间复杂度依然为O(n); 若是倒序,比较次数为 n-1+n-2+n-3+...+1 = n(n-1)/2,时间复杂度为O(n²)

public static int[] BubbleSort (int[] arr) {
    for (int i = 0; i < arr.length; i++) {            
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
   return arr;
}

选择

public static int[] SelectSort (int[] arr) {
    for (int i = 0; i < arr.length-1; i++) {            
        for (int j = i + 1; j < arr.length; j++) {
            int temp = arr[i];
            if (arr[i] > arr[j]) {
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

快排

寻找基准元素、分区

public int[] MySort (int[] arr) {
        quicksort(arr, 0, arr.length - 1);
        return arr;
    }
    
    public void quicksort(int[] list, int left, int right) {
        if (left < right) {
            int pivot = randompivot(list, left, right);
            quicksort(list,left, pivot-1);
            quicksort(list, pivot+1, right);
        }
    }
    
    public int randompivot(int[] list, int left, int right) {
        int first = list[left];
        while (left < right) {
            while (left < right && list[right] >= first) {
                right--;
            }
            swap(list, left, right);
            
            while(left<right && list[left] <= first) {
                left++;
            }
            swap(list, left, right);
        }
        return left;
    }
    public void swap(int[] list, int left, int right) {
        int temp = list[left];
        list[left] = list[right];
        list[right] = temp;
        
    }

二分查找

对于有序数组 nums,定位某一个元素 target 复杂度 O(log2n)

    public static int search(int[] nums, int target) {
		if (nums == null || nums.length == 0) return -1;	
		int left = 0;
		int right = nums.length - 1;
		while (left < right) {
			int mid = left + (right - left) / 2;
			if (nums[mid] < target) {
				left = mid + 1;
			} else {
				right = mid;
			}
		}		
		return nums[left] == target ? left : -1;
	}

栈与队列

用栈实现队列

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
        if (stack2.isEmpty()) {
        //如果 outstack 是空的,就把instack 全部 push 到 outstack 里
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        } 
        // 否则直接弹出
        return stack2.pop();
    }
}

字符串

最长无重复子串

    public int maxLength(int[] arr) {
        //用链表实现队列,队列是先进先出的
        Queue<Integer> queue = new LinkedList<>();
        int res = 0;
        for (int c : arr) {
            while (queue.contains(c)) {
                //如果有重复的,队头出队
                queue.poll();
            }
            //添加到队尾
            queue.add(c);
            res = Math.max(res, queue.size());
        }
        return res;
    }

数组

斐波那契数列

递归

public int Fibonacci(int n) {
        if (n <= 1) return n;
        return Fibonacci(n-1) + Fibonacci(n-2);
}

非递归

public int Fibonacci(int n) {
        if (n <= 1) return n;
        int first = 0;
        int second = 1;
        int temp;
        for (int i = 2; i <= n; i++){
            temp = second;
            second = first + second;
            first = temp;
        }
        return second;
    }

有序数组合并

public void merge(int A[], int m, int B[], int n) {

    int aPtr = m - 1, bPtr = n - 1;
//  两数组元素从右至左比较,大的去 A 尾部,直至有一方指针到头为止
    for (int ptr = m + n - 1; aPtr >= 0 && bPtr >= 0; ptr--){
        A[ptr] = A[aPtr] > B[bPtr] ? A[aPtr--] : B[bPtr--];
    }
//  A 指针先走完的情况,B 中剩余元素直接copy至 A 对应位置即可;
    while (bPtr >= 0){
        A[bPtr] = B[bPtr--];
    }

}

两数之和

public int[] twoSum (int[] numbers, int target) {
    Map<Integer, Integer> map = new HashMap<>();        
        for (int index = 0; index < numbers.length; index++) {
            int cur = numbers[index];
            if (map.containsKey(target-cur)) {
                return new int[]{map.get(target-cur)+1, index+1};
            }
            map.put(cur, index);
        }
    throw new RuntimeException("results not exits");
}

移除有序数组中的重复元素

链表

链表反转

public ListNode ReverseList(ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while (head != null) {
            next = head.next;//先保存 next 指针
            head.next = pre;//往前指
            pre = head;//往后平移
            head = next;//往后平移
        }
        return pre;
    }

判断是否有环

快慢指针

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) return false;
        ListNode fast = head;
        ListNode slow = head;
        //如果没环的话,快指针会先到链表尾部
        while (fast != null && fast.next !=null) {
            slow = slow.next;
            fast = fast.next.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
}

链表中环的入口节点

前提:快慢指针,快指针一次2步,慢指针一次1步,从X点出发,然后在Z点相会,求Y节点。

链表环.png

  1. 那么我们可以知道fast指针走过a+b+c+b
  2. slow指针走过a+b
  3. 那么 2*(a+b) = a+b+c+b
  4. 所以a = c
  5. 那么此时让 fast 回到起点,slow 依然停在z,两个同时开始走,一次走一步
  6. 那么它们最终会相遇在y点,正是环的起始点
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) return null;
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next !=null) {
            slow = slow.next;
            fast = fast.next.next;
            if (fast == slow) {
                fast = head;// fast回到起点
                while (fast != slow) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;     
            }
        }
        return null;
    }
}

链表中倒数第K个节点

1.先让first指针先走 K 步; 2.再让first和second指针同时走,first指针走到尾,则second相当于走到倒数第k个节点

public ListNode FindKthToTail (ListNode pHead, int k) {
        if (pHead == null) return pHead;
        ListNode first = pHead;        
        while (k-- > 0) {
            if (first == null) return null;
            first = first.next;
        }
        ListNode second = pHead;
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        return second;
    }

合并有序链表

递归

public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
        
        if (l1 == null || l2 == null) {
            return l1 == null ? l2 : l1;
        }
        if (l1.val <= l2.val) {
            l1.next = Merge(l1.next, l2);
            return l1;
        } else {
            l2.next = Merge(l1, l2.next);
            return l2;
        }
}

非递归

public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        // 哑结点(初始值为Null的节点)
        ListNode dummyNode = new ListNode(0);
        ListNode cur = dummyNode;        
        while (l1 != null && l2 != null) {
            if (l1.val > l2.val) {
                cur.next = l2;
                l2 = l2.next;
            } else {
                cur.next = l1;
                l1 = l1.next;
            }
            cur = cur.next;
        }
        cur.next = l1 == null ? l2 : l1;
        return dummyNode.next;
    }

两个链表的第一个公共结点

284295_1587394616610_37C15C411477833D2C2325823D927212.png

1、假设 链表A 长度为 a,链表B长度为 b2A 走完 a 再将指针指向BB走完b 再将指针指向A,那肯定会相遇;
3、即 a+c+b = b+c+a; (公共点后长度为 c)

此题和链表入口环的节点 题目题解一样的思路。
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
 
        if (pHead1 == null || pHead2 == null) return null;
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        
        while (p1 != p2) {
            p1 = p1 != null ? p1.next : pHead2;//到头了就指向 B 
            p2 = p2 != null ? p2.next : pHead1;//到头了就指向 A
        }
        return p1;
    }

删除有序链表中重复的元素1

public ListNode deleteDuplicates (ListNode head) {
        // write code here
        if (head == null) {
            return null;
        }
        ListNode cur = head;
        while(cur != null && cur.next != null) {
            if (cur.val == cur.next.val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }

删除有序链表中重复的元素2

public ListNode deleteDuplicates (ListNode head) {
        // write code here
        if (head == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while (cur.next != null && cur.next.next != null) {
            if (cur.next.val == cur.next.next.val) {
                int temp = cur.next.val;
                while (cur.next != null && cur.next.val == temp) {
                    cur.next = cur.next.next;
                }
            } else {
                cur = cur.next;
            }
        }
        return dummy.next;
    }

判断一个链表是否是回文结构

{1,2,3,4,4,3,2,1}

快慢指针思路,找到中点,然后慢指针后面的反转,快指针指向head

public boolean isPail (ListNode head) {
        //  快慢指针思路,快指针到达表尾部,慢指针到达中间位置,然后慢指针后面的反转,快指针指向head
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        // 链表为奇数个
        if (fast != null) {
            slow = slow.next;
        }
        // 反转后半部分链表
        slow = reverse(slow);
        // 快指针指向表头
        fast = head;
        while (slow != null) {
            if (slow.val != fast.val) {
                return false;
            } 
            slow = slow.next;
            fast = fast.next;
        }
        return true;

    }
    // 慢指针后半部分翻转
    public ListNode reverse(ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

二叉树

二叉树前、中、后序遍历

前:> 中左右 中:左中右 后:左右中

public int[] postorderTraversal (TreeNode root) {
        // write code here
        List<Integer> list = new ArrayList<Integer>();
        postorder(list, root);
        int[] res = new int[list.size()];
        for (int i=0;i<list.size();i++) {
            res[i] = list.get(i);
        }
        return res;
    }
    public void postorder(List<Integer> list, TreeNode root) {
        if (root == null) {
            return;
        }
        // 前序遍历:中左右
        list.add(root.val);
        postorder(list, root.left);
        postorder(list, root.right);

    }

判断是否是完全二叉树

若二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的叶子结点都连续集中在最左边,这就是完全二叉树。(第 h 层可能包含 [1~2h] 个节点)

度为1的节点只有左子树

    public boolean isFull(TreeNode root) {
        if (root == null) return false;
        Queue<TreeNode> queue = new LinkedList();
        boolean isLeaf = false;
        while(!queue.isEmpty()) {
            
            TreeNode node = queue.poll();
            if (isLeaf && !isLeaf(node)) {
                return false;
            }
            if (node.left != null) {
                queue.offer(node);
            } else if (node.right != null) {
                return false;
            }
            
            if (node.right != null) {
                queue.offer(node);
            } else {
                isLeaf = true;
            }
        }
        return true;
    }
    private boolean isLeaf(TreeNode node) {
        return (node.left == null) && (node.right == null);
    }

二叉树高度

递归

public int maxDepth (TreeNode root) {
        if(root == null) return 0;
        return 1+ Math.max(maxDepth(root.left), maxDepth(root.right));
    }

层序遍历👇🏻

层序遍历

 public ArrayList<ArrayList<Integer>> levelOrder (TreeNode root) {
        if (root == null) return new ArrayList();
        ArrayList list = new ArrayList();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();            
            ArrayList subList = new ArrayList();
            for (int i = 0; i < size;i++) {
                TreeNode node = queue.poll();
                subList.add(node.val);
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            list.add(subList);
        }
        return list;
    }

之字形层序遍历

public ArrayList<ArrayList<Integer>> zigzagLevelOrder (TreeNode root) {
        if (root == null) return new ArrayList();        
        ArrayList list = new ArrayList();
        Queue<TreeNode> queue = new LinkedList();
        queue.offer(root);
        while (!queue.isEmpty()) {
            ArrayList sublist = new ArrayList();//存储每一层节点
            for (int i = queue.size(); i >0;i--) {
                TreeNode node = queue.poll();//弹出队列中的节点
                if ((list.size()+1) %2 !=0) {//奇数层,尾部插入
                    sublist.add(node.val);
                } else {//偶数层 头插
                    sublist.add(0, node.val);
                }
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            list.add(sublist);
        }        
        return list;
    }

二叉树翻转(镜像)

        public TreeNode invertTree(TreeNode node) {
		if (node == null) {
			return null;
		}
		TreeNode temp = node.left;
		node.left = node.right;
		node.right = temp;
		invertTree(node.left);
		invertTree(node.right);
		return node;
	}