常见算法题

230 阅读1分钟

1、二分查找
右侧边界为length-1;
while left <= right 跳出条件为区间中没有数据了。 right = mid - 1; 寻找左侧边界,需要检查left大于length,left是否不等于target。
寻找右侧边界,需要检查right小于0,right是否不等于target。

2、快速排序

从数组中取第一个数作为pivot,大于该数的划分到右边,小于的划分到左边。双指针,while循环left < right。
while(left < right && nums [left] <= pivot) 找到左边大于pivot和右边小于pivot的数之后交换

//快速排序
public int[] quickSort(int[] nums, int k) {
    quickSelect(nums, 0, nums.length - 1);
    return nums;
}

//左右两边递归执行
public void quickSelect(int[] a, int l, int r) {
    if (l < r) {
        int pos = randomPartition(a, l, r);
        quickSelect(a, l, pos - 1);
        quickSelect(a, pos + 1, r);
    }

}

//随机在范围内选一个元素,交换到最右
public int randomPartition(int[] a, int l, int r) {
    Random random = new Random();
    int i = random.nextInt(r - l + 1) + l;
    swap(a, i, r);
    return partition(a, l, r);
}

//取最右元素作为基准,遍历nums数组,小于最右元素的值交换位置,最后将最右元素交换到应该的位置
public int partition(int[] nums, int left, int right) {
    int value = nums[right];
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (nums[j] <= value) {
            i++;
            swap(nums, i, j);
        }
    }
    swap(nums, i + 1, right);
    return i + 1;
}

public void swap(int[] nums, int index1, int index2) {
    int temp = nums[index1];
    nums[index1] = nums[index2];
    nums[index2] = temp;
}

3、寻找第K大的数

最大堆实现
PriorityQueue

代码是求最小K个数。

public static int[] smallestK(int[] arr, int k) {
    int[] vec = new int[k];
    if (k == 0) { // 排除 0 的情况
        return vec;
    }
    PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
        public int compare(Integer num1, Integer num2) {
            return num2 - num1;
        }
    });
    for (int i = 0; i < k; ++i) {
        queue.offer(arr[i]);
    }
    for (int i = k; i < arr.length; ++i) {
        if (queue.peek() > arr[i]) {
            queue.poll();
            queue.offer(arr[i]);
        }
    }
    for (int i = 0; i < k; ++i) {
        vec[i] = queue.poll();
    }
    return vec;
}

4、前序、中序、后序、层序遍历二叉树

前序

public List<Integer> orderTraversal(TreeNode root) {
  List<Integer> list = new ArrayList<>();
  if(root == null) return list;

  Stack<TreeNode> stack = new Stack<>();
  stack.push(root);

  while(!stack.isEmpty()) {
    TreeNode cur = stack.pop();
    if(cur.right != null) stack.push(cur.right);
    if(cur.left != null) stack.push(cur.left);
    list.add(cur.val);
  }

  return list;
}

中序

public List<Integer> inorderTraversal(TreeNode root) {
    Stack<TreeNode> stack = new Stack<>();
    List<Integer> list = new ArrayList<>();
    if(root == null) return list;
    
    TreeNode cur = root;
    while(!stack.isEmpty() || cur != null) {
        while(cur != null) {
          stack.push(cur);
          cur = cur.left;
        }

        cur = stack.pop();
        list.add(cur.val);
        cur = cur.right;
    }

    return list;
}

后序

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
//后序遍历:左 右 根 --> 根 右 左
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;

        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);

        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            res.add(node.val);
            if(node.left != null) stack.push(node.left);
            if(node.right != null) stack.push(node.right);
        }
        Collections.reverse(res);
        return res;
    }
}

5、反转链表
迭代法:
pre = null;
while中存一下下个节点next.

递归法:

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
}

6、判断链表是否有环

快慢指针,判断是否相遇。

寻找链表形成环的位置

同时从头出发,快慢指针相遇时,快指针从头继续出发,相遇位置即为形成环的位置,数学推导。

7、LRU实现

HashMap + 双向链表

Node
DoubleList: getSize\addLast\remove\removeFirst
LRUCathe: makeRecentLy\addRecentLy\deleteKey\removeLeastRecentLy

class Node {
    private int key, val;
    private Node pre, next;

    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}

class DoubleList {
    Node head, tail;
    private int size;

    public DoubleList() {
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.pre = head;
        size = 0;
    }

    public int getSize() {
        return size;
    }

    public void addLast(Node node) {
        node.pre = tail.pre;
        node.next = tail;
        tail.pre.next = node;
        tail.pre = node;
        size++;
    }

    public void remove(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
        size--;
    }

    public Node removeFirst() {
        if (head.next == tail) {
            return null;
        }
        Node first = head.next;
        remove(first);
        return first;
    }

}

class LRUCache {
    private DoubleList cache;
    private HashMap<Integer, Node> map;
    private int cap;

    public LRUCache(int cap) {
        this.cap = cap;
        map = new HashMap<>();
        cache = new DoubleList();
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        // 将该数据提升为最近使用的
        makeRecently(key);
        return map.get(key).val;
    }

    public void put(int key, int val) {
        if (map.containsKey(key)) {
            // 删除旧的数据
            deleteKey(key);
            // 新插入的数据为最近使用的数据
            addRecently(key, val);
            return;
        }

        if (cap == cache.getSize()) {
            // 删除最久未使用的元素
            removeLeastRecently();
        }
        // 添加为最近使用的元素
        addRecently(key, val);
    }

    /* 将某个 key 提升为最近使用的 */
    private void makeRecently(int key) {
        Node x = map.get(key);
        // 先从链表中删除这个节点
        cache.remove(x);
        // 重新插到队尾
        cache.addLast(x);
    }

    /* 添加最近使用的元素 */
    private void addRecently(int key, int val) {
        Node x = new Node(key, val);
        // 链表尾部就是最近使用的元素
        cache.addLast(x);
        // 别忘了在 map 中添加 key 的映射
        map.put(key, x);
    }

    /* 删除某一个 key */
    private void deleteKey(int key) {
        Node x = map.get(key);
        // 从链表中删除
        cache.remove(x);
        // 从 map 中删除
        map.remove(key);
    }

    /* 删除最久未使用的元素 */
    private void removeLeastRecently() {
        // 链表头部的第一个元素就是最久未使用的
        Node deletedNode = cache.removeFirst();
        // 同时别忘了从 map 中删除它的 key
        int deletedKey = deletedNode.key;
        map.remove(deletedKey);
    }

LinkedHashMap

    public class LRUCathe {
        int capacity;
        LinkedHashMap<Integer, Integer> cathe = new LinkedHashMap<>();
        public LRUCathe(int capticy) {
            this.capacity = capticy;
        }
        public int get(int key) {
            if (!cathe.containsKey(key)) {
                return -1;
            }
            makeItRecently(key);
            return cathe.get(key);
        }
        public void put(int key, int val) {
            if (cathe.containsKey(key)) {
                cathe.put(key, val);
                makeItRecently(key);
            }
            if (cathe.size() >= capacity) {
                int oldestKey = cathe.keySet().iterator().next();
                cathe.remove(oldestKey);
            }
            cathe.put(key, val);
        }
        private void makeItRecently(int key) {
            int val = cathe.get(key);
            cathe.remove(key);
            cathe.put(key, val);
        }
    }

8、两个栈实现队列

一个inStack
一个outStack
push进inStack
peek就判断outStack是否空,为空就转移inStack,不为空直接返回peek
pop先执行peek操作,然后pop outStack

9、队列实现栈

10、背包问题

11、买卖股票

12、打家劫舍