数据结构题目总结

数据结构题目专题

  数据结构的题目都比较死,套路比较单一,只要各个数据结构都熟悉它们的操作,就可以上手去干题目了。

  大概分为三种类型:顺序表类型、树类型、图类型。

  细分为:数组、链表、栈、队列、二叉树、无向图、有向图等。

2. Add Two Numbers

  

 

 

   比如这题,使用链表来表示一个整数,将两个数加起来然后返回一个链表,据说今日某条的笔试题有这道题。看着submit好像是三年前的,代码写得太丑了,抽空重写下再贴吧。

   思路就是把他俩反转,挨个加了之后返回一个链表。

19. Remove Nth Node From End of List

  

 

 

   要求去掉从末尾往前数n位的节点,好几种思路。比如可以把链表反转,顺序去掉,或者统计长度,对n取补,去掉算出来的位置就好。这题有坑,去掉第一个节点时候掉坑里了。

 

 

 

    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return null;
        }
        int len = 0 ;
        ListNode tmp = head;
        while (tmp != null){
            len ++;
            tmp = tmp.next;
        }

        int fromHead = len - n;
        if(fromHead < 1){
            head = head.next;
        }else {
            ListNode skip = head;
            for(int i=1;i<fromHead;i++){
                if(skip != null)
                    skip = skip.next;
            }
            if(skip != null){
                ListNode skipNext = skip.next;
                if(skipNext != null){
                    skip.next = skipNext.next;
                }
            }
        }
        return head;
    }

24. Swap Nodes in Pairs

  

 

 

   链表每两个节点反转,先做这题就会做k group了,自从用了LC,就喜欢上了绿色。

 

 

 

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode pre = new ListNode(-1);
        pre.next = head;
        ListNode change1 = null;
        ListNode change2 = null;
        int control = 0;
        ListNode runner = head;
        while(runner != null){
            if(control == 0){
                change1 = runner;
                control ++;
                runner = runner.next;
            }else if(control == 1){
                change2 = runner;
                runner = runner.next;
                if(pre.next == head){
                    head = change2;
                }
                pre.next = change2;
                change1.next = change2.next;
                change2.next = change1;

                pre = change1;

                control --;
            }
        }
        return head;
    }
}

25. Reverse Nodes in k-Group

  

 

 

   这个题的题意就是一个链表可以根据k分成它的等差区间:(0,k-1),(k,2k-1).....

   把这些按区间反转,没有想到优化方案。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null){
            return null;
        }
        if(k == 0){
            return head;
        }
        int control = 1;
        ListNode pre1 = new ListNode(-1);
        pre1.next = head;
        ListNode change1 = null;
        ListNode change2 = null;

        ListNode runner = head;
        while(runner != null){
            if(control == 1){
                change1 = runner;
            }else if(control == k){
                change2 = runner;
                runner = runner.next;
                if(pre1.next == head){
                    head = change2;
                }
                ListNode temp = change1;
                ListNode revertHead = null;
                while(temp != runner){
                    ListNode cur = temp;
                    temp = temp.next;
                    if(revertHead == null) {
                        cur.next = runner;
                    }else {
                        cur.next = revertHead;
                    }
                    revertHead = cur;
                }
                pre1.next = revertHead;

                pre1 = change1;
                control = 1;
                continue;
            }

            control ++;
            runner = runner.next;
        }
        return head;
    }

}

43. Multiply Strings

  

 

 

   大整数乘法,别看这玩意的案例才5万多,坑多得很,使用数组模拟列竖式计算,每个数组位只记十位数的一位。

class Solution {
    public String multiply(String num1, String num2) {
        if(num1 == null || num2 ==null || num1.isEmpty() || num2.isEmpty()
            || "0".equals(num1) || "0".equals(num2)){
            return "0";
        }
        int[] resultArray = new int[1000];
        Arrays.fill(resultArray,-1);
        int baseCode = 48;
        int bit = 0;
        for(int i=num2.length()-1;i>=0;i--){
            int j=num1.length()-1;
            int currentBitBase = num2.charAt(i) - baseCode;
            int eachK = bit;
            while(j>=0){
                int curResult = (num1.charAt(j) - baseCode)*currentBitBase ;
                if(resultArray[eachK] < 0){
                    resultArray[eachK] = curResult ;
                }else {
                    resultArray[eachK] += curResult;
                }
                eachK ++;
                j--;
            }
            bit ++;
        }
        int lastIndex ;
        int carry = 0;
        for(lastIndex = 0;lastIndex<resultArray.length;lastIndex++){
            if(resultArray[lastIndex] < 0)
                break;
            else {
                resultArray[lastIndex] += carry;
                carry = 0;
                if(resultArray[lastIndex] >= 10){
                    carry = resultArray[lastIndex]/10;
                    resultArray[lastIndex] %= 10;
                }
            }
        }
        if(carry > 0){
            resultArray[lastIndex] = carry;
        }else {
            lastIndex--;
        }

        StringBuilder intValue = new StringBuilder();
        for(;lastIndex>=0;lastIndex--){
            intValue.append(resultArray[lastIndex]);
        }

        return intValue.toString();
    }
}

 

49. Group Anagrams

  

 

 

    按字母分类,使用哈希表。

    每次把拿到的字符按字典序排序一下,作为key。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        if (strs.length == 0) return new ArrayList();
        Map<String, List<String>> ans = new HashMap<>();
        for (String s : strs) {
            char[] carr = s.toCharArray();
            Arrays.sort(carr);
            StringBuilder sb = new StringBuilder();
            for(char c : carr){
                sb.append(c);
            }
            if(!ans.containsKey(sb.toString())){
                ans.put(sb.toString(),new ArrayList<String>());
            }
            ans.get(sb.toString()).add(s);
        }
        return new ArrayList(ans.values());
    }
}

23. Merge k Sorted Lists

  

 

 

   将一个链表数组,合成一个链表。使用优先队列,把所有节点全扔进去,然后再拿出来。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        PriorityQueue<Integer> queue = new PriorityQueue();
        for(ListNode h : lists){
            ListNode temp = h;
            while(temp != null){
                queue.add(temp.val);
                temp = temp.next;
            }
        }
        ListNode head = null;
        ListNode tail = null;
        while(!queue.isEmpty()){
            if(head == null){
                head = new ListNode(queue.poll());
                tail = head;
            }else {
                tail.next = new ListNode(queue.poll());
                tail = tail.next;
            }
        }
        return head;
    }
}

57. Insert Interval

  

 

 

   区间插入,虽然是hard但是略水,没beat 100,提高了10倍也还是20%的水平,无爱了。

class Solution {
    /**
     * 区间插入
     *
     */
    public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> orgin = new ArrayList<>(Arrays.asList(intervals));
        boolean append = false;
        for(int i=0;i<orgin.size();i++){
            if(newInterval[0]<=orgin.get(i)[0]){
                orgin.add(i,newInterval);
                append = true;
                break;
            }
        }
        if(!append)
            orgin.add(newInterval);

        int[][] newIntervals = orgin.toArray(new int[orgin.size()][]);

        List<int[]> mergeList = new ArrayList<>(newIntervals.length);
        for (int[] newInterval1 : newIntervals) {
            if (mergeList.size() > 0) {
                int[] last = mergeList.get(mergeList.size() - 1);
                if (last[1] >= newInterval1[0]) {
                    last[1] = Math.max(last[1], newInterval1[1]);
                    mergeList.set(mergeList.size() - 1, last);
                } else {
                    mergeList.add(newInterval1);
                }
            } else {
                mergeList.add(newInterval1);
            }
        }

        return mergeList.toArray(new int[mergeList.size()][]);
    }
}

 

61. Rotate List  

  

 

  题意是链表像一个环一样,每个位置向右滚动k个位置。思路就是将它弄成环,然后找到新的head,再断开。

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null || k == 0){
            return head;
        }
        int len = 1;
        ListNode runner = head;
        while(runner.next != null){
            len ++;
            runner = runner.next;
        }
        runner.next = head;

        int runTimes = len - (k % len);

        ListNode pre = head;
        ListNode newHead = head.next;

        for(int i=1;i<runTimes;i++){
            pre = pre.next;
            newHead = newHead.next;
        }
        pre.next = null;

        return newHead;
    }
}

86. Partition List

   

 

 

   被快排的partition荼毒了,它的要求是将大于等于x的划分成一个链表,小于x的分成另一个链表,然后再进行拼接。注意,这里的链表顺序是不能变的。

  比如小于3的,就是1,2,2;大于等于3的是4,3,5;然后他们合起来是1,2,2,4,3,5。

class Solution {
    public ListNode partition(ListNode head, int x) {
      if(head == null)
            return head;
        ListNode smallHead = null;
        ListNode small = null;
        ListNode big = null;
        ListNode bigHead = null;
        while (head != null){
            ListNode current = head;
            head = head.next;
            if(current.val < x){
                if(small == null){
                    small = current;
                    smallHead = small;
                }else {
                    small.next = current;
                    small = small.next;
                }
                small.next = bigHead;
            }else {
                if(big == null){
                    big = current;
                    bigHead = big;
                }else {
                    big.next = current;
                    big = big.next;
                }
                big.next = null;
            }

        }
        if(small != null){
            small.next = bigHead;
        }
        return smallHead == null ? bigHead : smallHead;
    }
}

80. Remove Duplicates from Sorted Array II

  

 

 

   这题是给定一个数组,去掉它的duplicate值,然后返回长度,它的长度是原数组从头开始算的长度,所有的操作都在原数组上执行。

   竟然还有人比我快,O(n)的系数比我小么。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length < 3){
            return nums.length;
        }
        int len = 0;
        int duplicates = 0;
        int currentVal = nums[0];
        for(int i=0;i<nums.length;i++){
            if(nums[i] == currentVal && duplicates < 2){
                duplicates ++;
                nums[len++] = nums[i];
            }else if(nums[i] == currentVal){
                continue;
            }else if(nums[i] != currentVal){
                currentVal = nums[i];
                duplicates = 1;
                nums[len++] = nums[i];
            }
        }
        return len; 
    }
}

102. Binary Tree Level Order Traversal

  

 

 

   二叉树层序遍历。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if(root == null){
            return result;
        }

        Queue<TreeNode> currentLevel = new ArrayDeque<>();
        currentLevel.add(root);
        Queue<TreeNode> nextLevel = new ArrayDeque<>();

        List<Integer> levelResult = new ArrayList<>();
        while (!currentLevel.isEmpty()){
            TreeNode node = currentLevel.poll();
            levelResult.add(node.val);
            if(node.left != null){
                nextLevel.add(node.left);
            }
            if(node.right != null){
                nextLevel.add(node.right);
            }
            if(currentLevel.isEmpty()){
                result.add(new ArrayList<>(levelResult));
                levelResult.clear();
                currentLevel = nextLevel;
                nextLevel = new ArrayDeque<>();
            }
        }
        return result;
    }
}

103. Binary Tree Zigzag Level Order Traversal

  

 

 

   二叉树z字型遍历,花式。

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
                List<List<Integer>> result = new ArrayList<>();
        if(root == null){
            return result;
        }

        Stack<TreeNode> currentStack = new Stack<>();
        Stack<TreeNode> nextStack = new Stack<>();

        currentStack.push(root);
        List<Integer> levelResult = new ArrayList<>();
        while (!currentStack.isEmpty()){
            TreeNode node = currentStack.pop();
            levelResult.add(node.val);
            if(result.size() % 2 == 0){
                if(node.left != null){
                    nextStack.push(node.left);
                }
                if(node.right != null){
                    nextStack.push(node.right);
                }
            }else {
                if(node.right != null){
                    nextStack.push(node.right);
                }
                if(node.left != null){
                    nextStack.push(node.left);
                }

            }
            if(currentStack.isEmpty()){
                result.add(new ArrayList<>(levelResult));
                levelResult.clear();
                currentStack = nextStack;
                nextStack = new Stack<>();
            }
        }
        return result;
    }
}

144. Binary Tree Preorder Traversal

  

 

 

   二叉树前序遍历,也就是根->左->右。学到了俩单词,Preorder,Traversal。

   最后还问你,能整成循环么?当然不能,天天加料加需求。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        preorder(root,result);
        return result;
    }
    void preorder(TreeNode node,List<Integer> result){
        if(node == null){
            return;
        }
        result.add(node.val);
        preorder(node.left,result);
        preorder(node.right,result);
    }
}
    void preorderIteratively(TreeNode node,List<Integer> result){

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

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

 

145. Binary Tree Postorder Traversal

  

 

 

   水题,白给hard。后序遍历,不过循环做还是难的。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null)
            return result;
        postorderIteratively(root,result);
        return result;
    }
    /**
     * post order means left->right->root
     *
     */
    void postorderIteratively(TreeNode node,List<Integer> result){
        Stack<TreeNode> stack = new Stack<>();
        TreeNode curr = node;
        while(!stack.isEmpty() || curr != null){
            if(curr != null){
                stack.push(curr);
                result.add(0, curr.val);
                curr = curr.right;
            }else{
                TreeNode top = stack.pop();
                curr = top.left;
            }
        }
    }
}

94. Binary Tree Inorder Traversal

  

 

 

   中序遍历。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(!stack.isEmpty() || cur != null){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else {
                TreeNode n = stack.pop();
                result.add(n.val);
                cur = n.right;
            }
        }

        return result;
    }
}

 

 

133. Clone Graph

   

 

   克隆一个无向图,实际上就是遍历,图的定义就是这么简洁。遍历图一定要有visitd标识。

   深度优先,就是使用栈。每次将未访问的邻居节点加入stack。

   广度优先,使用队列。

class Node {
    public int val;
    public List<Node> neighbors;

    public Node() {}

    public Node(int _val,List<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
class Solution {
    /**
     * 克隆一个图
     */
    public Node cloneGraph(Node node) {
        return dfs(node);
    }

    /**
     * 深度优先拷贝
     */
    Node dfs(Node node){
        if(node == null){
            return null;
        }
        Stack<Node> stack = new Stack<>();
        Set<Node> visited = new HashSet<>();
        Map<Integer,Node> newGraph = new HashMap<>();
        newGraph.put(node.val,cloneGraphNode(node));
        visited.add(node);
        stack.push(node);
        while (!stack.isEmpty()){
            Node cur = stack.pop();
            Node newNode = newGraph.get(cur.val);
            if(newNode == null){
                newNode = cloneGraphNode(cur);
                newGraph.put(newNode.val,newNode);
            }
            for(Node n : cur.neighbors){
                if(!visited.contains(n)){
                    stack.push(n);
                    visited.add(n);
                }
                Node nn = newGraph.get(n.val);
                if(nn == null){
                    nn = cloneGraphNode(n);
                    newGraph.put(nn.val,nn);
                }
                newNode.neighbors.add(nn);
            }

        }
        return newGraph.get(node.val);
    }
    Node cloneGraphNode(Node n){
        if(n == null){
            return null;
        }
        return new Node(n.val,new ArrayList<>());
    }
}
class Solution {
    /**
     * 克隆一个图
     */
    public Node cloneGraph(Node node) {
        return bfs(node);
    }

    /**
     * 广度优先拷贝
     */
    Node bfs(Node node){
        if(node == null){
            return null;
        }
        Queue<Node> queue = new ArrayDeque<>();
        Set<Node> visited = new HashSet<>();
        Map<Integer,Node> newGraph = new HashMap<>();
        queue.add(node);
        while (!queue.isEmpty()){
            Node cur = queue.poll();
            if(visited.contains(cur))
                continue;
            Node newNode = null;
            if((newNode = newGraph.get(cur.val)) == null){
                newGraph.put(cur.val,newNode=cloneGraphNode(cur));
            }
            for(Node n :cur.neighbors){
                Node nn = newGraph.get(n.val);
                if(nn == null){
                    newGraph.put(n.val,nn = cloneGraphNode(n));
                }
                newNode.neighbors.add(nn);
                if(!visited.contains(n)){
                    queue.add(n);
                }
            }
            visited.add(cur);
        }
        return newGraph.get(node.val);
    }
    Node cloneGraphNode(Node n){
        if(n == null){
            return null;
        }
        return new Node(n.val,new ArrayList<>());
    }
}

 

树状数组(Binary Indexed Tree)

  这是一种非常巧妙的数据结构,是为了解决前缀和n次询问的产生的。

  有一个数组n,它是一个离散的列,要求他的前i项和怎么办。

  第一种解法,就是for循环,从0加到i,这样的复杂度是O(n)。

  第二种解法,使用一个额外的数组,去保存前n项和。这样,询问的复杂度是O(1),但是更新的复杂度是O(n)。

  有一种解法,是树状数组。它的询问和更新都能到达O(logn)

 

   它的原理和证明,我不是很能理解,但是它这种数据结构是用来计算前n项和的n次询问的,这样的复杂度能到达O(logn),并且在支持动态更新的时候非常良好。

   实现这种数据结构很简单,只需要三个方法。lowbit方法,是计算下一个值所在位置;update是更新数组,getSum 或者 平时叫的 query 是查询前n项和。

 int lowbit(int x){
        return x & (-x);
    }
    void update(int[] array,int x,int k){
        int i = x + 1;
        while(i<array.length){
            array[i] += k;
            i += lowbit(i);
        }
    }
    int getSum(int[] array,int x){
        int res = 0;
        int i = x + 1;
        while (i>0){
            res += array[i];
            i -= lowbit(i);
        }
        return res;
    }

 

   这道题,使用前n项和去解。

public class ReversePairs {
    public void test(){
        int[] nums1 = {1,3,2,3,1};
        // 2
        System.out.println(reversePairs(nums1));
        int[] nums2 = {2,4,3,5,1};
        // 3
        System.out.println(reversePairs(nums2));
        int[] nums3 = {5,4,3,2,1,2,1};
        // 8
        System.out.println(reversePairs(nums3));
        // 0
        int[] nums4 = {2147483647,2147483647,2147483647,2147483647,2147483647,2147483647};
        System.out.println(reversePairs(nums4));
    }

    /**
     * binary indexed tree
     * build a BIT count nums[i]/2
     * rule : i<j ,nums[i] > 2*nums[j]
     */
    public int reversePairs(int[] nums) {
        int res = 0;
        int n = nums.length;
        // for binary search
        long[] copy = new long[n];
        for(int i=0;i<nums.length;i++){
            copy[i] = nums[i];
        }
        Arrays.sort(copy);
        int[] BIT = new int[n+1];
        for (int i = 0; i < n; ++i) {
            // for current i
            // we count 2*nums[i]+1 for each nums[i]
            res += getSum(BIT,copy.length - 1) - getSum(BIT,binarySearch(copy, 2 * (long)nums[i] + 1) - 1);
            update(BIT,binarySearch(copy, (long)nums[i]),1);
        }
        return res;
    }
    int lowbit(int x){
        return x & (-x);
    }
    void update(int[] array,int x,int k){
        int i = x + 1;
        while(i<array.length){
            array[i] += k;
            i += lowbit(i);
        }
    }
    int getSum(int[] array,int x){
        int res = 0;
        int i = x + 1;
        while (i>0){
            res += array[i];
            i -= lowbit(i);
        }
        return res;
    }
    int binarySearch(long[] sortArray,long value){
        int n = sortArray.length;
        int l = 0;
        int r = n - 1;
        while (l + 1 < r) {
            int m = l + (r - l) / 2;
            if (sortArray[m] >= value) {
                r = m;
            } else {
                l = m;
            }
        }
        if (sortArray[l] >= value) {
            return l;
        } else if (sortArray[r] >= value) {
            return r;
        } else {
            return r + 1;
        }
    }
}

 

posted @ 2019-10-11 17:57  天目山电鳗  阅读(715)  评论(0编辑  收藏  举报