【菜狗刷题】数据结构篇

155 阅读2分钟

内容学习自:www.bilibili.com/video/BV1sy…

数组

常见操作

 // 创建数组的常用操作:元素可以个数可以动态变化
 ArrayList<Integer> = new ArrayList<>();
     //添加:不用扩容 - O(1) / 需要扩容 - O(n) 
     .add()
     //访问:O(1)
     .get( int  index) 
     //查找元素:O(n)
     .contains( Object obj)   
     //删除:O(n)
     .remove( Object obj)
     //更新元素:O(1)   
     .set( int index , Object obj)
     //数组大小
     .size()  
     //数组排序:O( NlogN)
     Collections.sort()
     Collections.sort(arr,Collections.reverseOrder())    
 ​
 // 创建数组        
 int[] c = new int[3];
 int[] a = {1,2,3};
 int[] b = new int[]{1,2,3};
     //数组排序
     Arrays.sort( )

题目

  • 485. 最大连续 1 的个数 - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public int findMaxConsecutiveOnes(int[] nums) {
             
             if(null == nums){
                 return 0;
             }
             
             //最大连续1的个数
             int max = 0;
             //标志位:如果不为0,则说明上一个元素是1 + 当前已有的连续1个数
             int temp = 0;
             for(int i = 0;i < nums.length;i++){
                 if(1 == nums[i]){
                     max++;
                 }else{
                     max = max > temp ? max : temp;
                     temp = 0;
                 }
             }
             
             max = max > temp ? max : temp;
             return max;
         }
     }
    

    复杂度分析

    • 时间复杂度:O(n)。n是数组的长度。需要遍历数组一次。
    • 空间复杂度:O(1)。
  • 283. 移动零 - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public void moveZeroes(int[] nums) {
             
             int head = 0,index = 0;
             //找到第一个0的位置
             while(head < nums.length && 0 != nums[head]){
                 head++;
                 index = head;
             }
     ​
            //数组存在非0元素
            while(head < nums.length){
                //当前元素为0
                 if(0 == nums[index]){
                     //寻找后面第一个非0元素的位置
                     while(head < nums.length && 0 == nums[head]){
                             head++;
                     }
                     //找到了
                     if(head < nums.length){
                         nums[index++] = nums[head];
                         nums[head++] = 0;
                     }else{
                         //没找到:后面的元素全部都是0
                         return;
                     }
                 }else{
                     index++;
                 }
            } 
             
         }
     }
    

    复杂度分析

    • 时间复杂度:O(n)。n是数组的长度。需要遍历数组一次。
    • 空间复杂度:O(1)。
  • 27. 移除元素 - 力扣(LeetCode) (leetcode-cn.com)

    若要求非val元素保证顺序:可以利用上一题的解法(快慢指针),寻找下一个不是val的元素进行替代。但是此题没有要求顺序,那么也可以利用相对指针。

     class Solution {
         public int removeElement(int[] nums, int val) {
     ​
             if(null == nums || nums.length == 0){
                 return 0;
             }
             int head = 0,tail = nums.length-1;
     ​
             // 1 遍历数组
             while(head <= tail){
                 //2 是val
                 if(nums[head] == val){
                     //2.1 从后往前找,不是val的元素
                     while(tail > head && nums[tail] == val){
                         tail--;
                     }
                     //2.1.1 没找到:当前元素和其后的元素都是val
                     if(tail <= head){
                         return head;
                     }else{//2.1.2 找到了
                         nums[head++] = nums[tail--];
                     }
                 }else{//3 不是val,继续判断下一个元素
                     head ++;
                 }
             }
             return head;
             
         }
     }
    

链表

常用操作

 LinkedList<Integer> list = new LinkedLisr<>()
     //加在尾部 O(1) 
     .add(Object obj)
     .add(int index,Object obj)
     //访问元素 O(n) 
     .indexOf(Object obj)
     //更新元素
     .set(int index,Object obj)
     //删除元素 O(1) 
     .remove(int index)
     //长度
     .size()     

题目

  • 203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com)

    • 创建一个结点,它的下一个结点指向 head头节点

    • 遍历链表,从新创建的结点开始,

      • 如果下一个结点为val,利用循环往后寻找不是val的第一个结点

        • 删除中间所有值是val的结点
        • 移动链表,指向下一个结点
      • 如果当前结点的下一个结点为val,那么移动链表,指向下一个结点

     class Solution {
         public ListNode removeElements(ListNode head, int val) {
     ​
             if(null == head){
                 return head;
             }
     ​
             ListNode current = new ListNode(-1,head);
             ListNode dummy = current;
             while(null != current && null != current.next){ 
                 ListNode next = current.next;
                 while(null != next && next.val == val){
                     next = next.next;
                 }
                 current.next = next;
                 current = current.next;
     ​
             }
     ​
             return dummy.next;    
         }
     }
    
  • 206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)

     //方法一:
     class Solution {
         public ListNode reverseList(ListNode head) {
             
             ListNode pre = null,next = null;
             ListNode current = head;
     ​
             while(null != current){
                 //改变指针指向
                 next = current.next;
                 current.next = pre;
                 //移动链表
                 pre = current;
                 current = next;
             }
             
             return pre;
         }
     }
     ​
     //方法二:
     class Solution {
         public ListNode reverseList(ListNode head) {
           
             // 1 终止条件
             if(null == head || null == head.next){
                 return head;
             }
             // 2 大问题的答案
             ListNode r_head = reverseList(head.next);
             // 3 当前问题的解答
             head.next.next = head;
             head.next = null;
             return r_head;   
         }
     }
    
  1. 思考的时候多画图,可以清晰很多
  2. 边界情况考虑,看是否可以合并到一般情况

队列和栈

常用操作

常用操作QueueStack
增加元素add(Object obj)push(Object obj)
获取元素peek()peek()
删除元素,并返回poll()pop()
大小size()size()
是否为空 【边遍历边判断】isEmpty()isEmpty()

操作

  • 933. 最近的请求次数 - 力扣(LeetCode) (leetcode-cn.com)

     class RecentCounter {
         //创建队列
         Queue queue = new LinkedList<Integer>();
     ​
         public RecentCounter() {   
         }
         
         public int ping(int t) {
             this.queue.add(t);
             while(!queue.isEmpty()){
                 Integer element = (Integer)queue.peek();
                 if(t-3000 <= element){
                     return queue.size();
                 }else{
                     queue.poll();
                 }
             }
             return 0;
         }
     }
    
  • 20. 有效的括号 - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public boolean isValid(String s) {
     ​
             //定义多有括号对应关系
             HashMap<Character,Character> map = new HashMap();
             map.put('(',')');
             map.put('[',']');
             map.put('{','}');
     ​
             //定义栈结构
             Stack stack = new Stack<Character>();
             
             //将所有字符入栈
             for(int i = 0 ;i < s.length();i++){
                 char c = s.charAt(i);
                 //如果是左括号,入栈
                 if(map.containsKey(c)){
                     stack.push(c);
                 }else{
                     //如果是右括号,则出栈上一个元素
                     if(stack.isEmpty() || (c != map.get(stack.peek()))){
                         return false;
                     }else{
                         stack.pop();
                     }
                 }    
             }  
             if(!stack.isEmpty()){
                 return false;
             }
             return true;
         }
     }
    
  • 496. 下一个更大元素 I - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public int[] nextGreaterElement(int[] nums1, int[] nums2) {
     ​
             HashMap<Integer,Integer> map = new HashMap<>();
             Stack<Integer> stack = new Stack();
     ​
             //遍历nums2,找出所有元素对应的下一个最大值
             for(int i = 0;i < nums2.length;i++){
                 if(stack.isEmpty()){
                     stack.push(nums2[i]);
                 }else{
                     while(!stack.isEmpty() && stack.peek() < nums2[i]){
                         map.put(stack.pop(),nums2[i]);
                     } 
                     stack.push(nums2[i]);
                 }
             }   
             
             while(!stack.isEmpty()){
                 map.put(stack.pop(),-1);
             }
     ​
             // 遍历 nums1 得到结果集
             int[] res = new int[nums1.length];
             for (int i = 0; i < nums1.length; i++) {
                 res[i] = map.get(nums1[i]);
             }
             return res;
         }
     }
    

HashMap

常见操作

     HashMap<Integer,Integer> map = new HashMap<>();
         //添加元素
         .put(1,1)
         //更新元素(利用唯一性)
         .put(1,3)
         //删除元素
         .remove(key)
         //获取元素
         .get(key)    
         //判断key是否存在
         .containsKey()
         //大小
         .size()
         //是否为空
         .isEmpty()

题目

  • 217. 存在重复元素 - 力扣(LeetCode) (leetcode-cn.com)

     //方法一
     class Solution {
         public boolean containsDuplicate(int[] nums) {
             
             HashSet<Integer> set = new HashSet<>();
     ​
             for(int i = 0;i < nums.length;i++){
                 set.add(nums[i]);
             }
     ​
             if(nums.length == set.size()){
                 return false;
             }
             return true;      
         }
     }
     //方法二
     class Solution {
         public boolean containsDuplicate(int[] nums) {
             
             HashMap<Integer,Object> map = new HashMap<>();
     ​
             for(int i = 0;i < nums.length;i++){
                 map.put(nums[i],null);
             }
     ​
             if(nums.length == map.size()){
                 return false;
             }
             return true;
         }
     }
    
  • 389. 找不同 - 力扣(LeetCode) (leetcode-cn.com)

     //方法一
     class Solution {
         public char findTheDifference(String s, String t) {
     ​
             HashMap<Character,Integer> map = new HashMap<>();
             Character key;
             int value;
     ​
             for(int i = 0;i < s.length();i++){
                 key = s.charAt(i);
                 //已经出现过的key
                 if(!map.containsKey(key)){
                     map.put(key,1);
                 }else{
                     value = map.get(key)+1;
                     map.put(key,value);               
                 }
             }
     ​
             for(int i = 0;i < t.length();i++){
                 key = t.charAt(i);
                 //map中有的key
                 if(map.containsKey(key)){
                     value = map.get(key) - 1;
                     if(value < 0){
                         return key;
                     }
                     map.put(key,value);
                 }else{
                     //map中不存在的key
                     return key;
                 }
             }
             return t.charAt(t.length()-1);
         }
     }
     //方法二
     class Solution {
         public char findTheDifference(String s, String t) {
             int result = 0;
             //利用异或运算
             for(int i = 0;i < s.length();i++){
                 result ^= s.charAt(i);
             }
             for(int i = 0;i < t.length();i++){
                 result ^= t.charAt(i);
             }
             return (char)result;
         }
     }
    
  • 496. 下一个更大元素 I - 力扣(LeetCode) (leetcode-cn.com)

【唯一性】

  • 判断是否有重复元素:大小是否有变化
  • 找不同

【利用异或运算】

  • 提高运算效率

HashSet

常见操作

     HashSet<Integer> set = new HashSet<>();
         //添加元素
         .ddd(1)
         //删除元素
         .remove(key) 
         //判断key是否存在
         .contains( )
         //大小
         .size()
         //是否为空
         .isEmpty()

题目

  • 217. 存在重复元素 - 力扣(LeetCode) (leetcode-cn.com)

  • 705. 设计哈希集合 - 力扣(LeetCode) (leetcode-cn.com)

    设计的时候要记得保证各种操作的时间复杂度

     class MyHashSet {
     ​
         int [] arr = new int[1000009];
     ​
         /** Initialize your data structure here. */
         public MyHashSet() {
     ​
         }
         
         // void add(key) 向哈希集合中插入值 key 
         public void add(int key) {
             this.arr[key] = 1;
         }
         
         public void remove(int key) {
             arr[key] = 0;
         }
         
         /** Returns true if this set contains the specified element */
         public boolean contains(int key) {
             if(arr[key] == 1){
                 return true;
             }else{
                 return false;
             }
         }
     }
    

相关概念

  • 完全二叉树:每一层的结点从左到右依次
  • 满二叉树:每一层的结点数都达到最大值
  • 深度 高度 层

二叉树的遍历

  • 144. 二叉树的前序遍历 - 力扣(LeetCode) (leetcode-cn.com)

     //方法一:利用栈,后进先出
     class Solution {
         public List<Integer> preorderTraversal(TreeNode root) {
     ​
             if(null == root){
                 return new ArrayList<>();
             }
     ​
             TreeNode node;
             ArrayList<Integer> result = new ArrayList<>();
             Stack<TreeNode> stack = new Stack<>();
             //将头节点入栈,
             stack.push(root);
             while(!stack.isEmpty()){
                 //打印栈顶
                 node = stack.pop();
                 result.add(node.val);
                 //让右左结点依次入栈(后进先出)
                 if(null != node.right){
                     stack.push(node.right);
                 }
                 if(null != node.left){
                     stack.push(node.left);
                 }
             }
             return result;
         }
     }
     //方法二:递归
     class Solution {
         public List<Integer> preorderTraversal(TreeNode root) {
     ​
             if(null == root){
                 return new ArrayList<>();
             }
     ​
             List<Integer> result_left = preorderTraversal(root.left);
             List<Integer> result_right = preorderTraversal(root.right);
     ​
             ArrayList<Integer> result =  new ArrayList<>();
             result.add(root.val);
             result.addAll(result_left);
             result.addAll(result_right);
             
             return result;
         }
     }
    
  • 94. 二叉树的中序遍历 - 力扣(LeetCode) (leetcode-cn.com)

     //方式一
     class Solution {
         public List<Integer> inorderTraversal(TreeNode root) {
     ​
             if(null == root){
                 return new ArrayList<>();
             }
             //[最左所有的结点沿途的结点入栈]
             Stack<TreeNode> stack = new Stack<>();
             while(null != root){
                 stack.push(root);
                 root = root.left;
             }
             TreeNode node;
             ArrayList<Integer> result =  new ArrayList<>();
             while(!stack.isEmpty()){
                 //栈顶出栈
                 node = stack.pop();
                 result.add(node.val);
                 //栈顶的右孩子入栈
                 if(null != node.right){
                     stack.push(node.right);
                     node = node.right;
                     //栈顶的右孩子入栈 [最左所有的结点沿途的结点]入栈
                     while(null != node.left){
                         node = node.left;
                         stack.push(node);
                     }
                 }
             } 
             return result;
         }
     }
     //方式二
     class Solution {
         public List<Integer> inorderTraversal(TreeNode root) {
             if(null == root){
                 return new ArrayList<>();
             }
     ​
             List<Integer> result_left = inorderTraversal(root.left);
             List<Integer> result_right = inorderTraversal(root.right);
     ​
             ArrayList<Integer> result =  new ArrayList<>();
             result.addAll(result_left);
             result.add(root.val);
             result.addAll(result_right);
             
             return result;
         }
     }
    
  • 145. 二叉树的后序遍历 - 力扣(LeetCode) (leetcode-cn.com)

     //方法一
     class Solution {
         public List<Integer> postorderTraversal(TreeNode root) {
     ​
             if(null == root){
                 return new ArrayList<>();
             }
             //一开始和前序遍历是一致的:都是最左路径上的结点
             Stack<TreeNode> stack = new Stack<>();
             while(null != root){
                 stack.push(root);
                 root = root.left;
             }
             //上一个结点时当前的结点的右节点,则当前结点打印
             TreeNode pre = null;
             TreeNode current;
             ArrayList<Integer> result =  new ArrayList<>();
             while(!stack.isEmpty()){
                 current = stack.peek();
                 //如果是叶子结点 || 上一个结点是栈顶的右子节点
                 if(null == current.right || pre == current.right){
                         pre = stack.pop();
                         result.add(pre.val);
                 }else{
                     current = current.right;
                     stack.push(current);
                     while(null != current.left){
                         current = current.left;
                         stack.push(current);
                     }
                 }
             }
             return result;
         }
     }
     //方法二
     class Solution {
         public List<Integer> postorderTraversal(TreeNode root) {
             if(null == root){
                 return new ArrayList<>();
             }
     ​
             List<Integer> result_left = postorderTraversal(root.left);
             List<Integer> result_right = postorderTraversal(root.right);
     ​
             ArrayList<Integer> result =  new ArrayList<>();
             result.addAll(result_left);
             result.addAll(result_right);
             result.add(root.val);
             
             return result;
         }
     }
    

利用栈,单调栈 || 双栈,出栈的时机(打印)

常见操作

 //最小堆
 PriorityQueue<Integer> minheap = new PriorityQueue<>();
 //最大堆
 PriorityQueue<Integer> maxheap = new PriorityQueue<>(Collections.reverseOrder());   
     //添加元素
     .add() | .offer()
     //获得堆顶元素
     .peek()
     //删除元素
     .poll() | .remove()
     //堆的大小
     .size()
     //堆是否为空
     .isEmpty()

题目

  • 215. 数组中的第K个最大元素 - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public int findKthLargest(int[] nums, int k) {
             if(nums.length < k){
                 return -1;
             }
             //创建最大堆
             PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
             //添加元素
             for(int i = 0;i < nums.length;i++){
                 maxHeap.add(nums[i]);
             }
             //通过获取第k个堆顶元素
             for(int i = 1;i < k;i++){
                 maxHeap.poll();
             }     
             return maxHeap.peek();
         }
    
  • 692. 前K个高频单词 - 力扣(LeetCode) (leetcode-cn.com)

     class Solution {
         public List<String> topKFrequent(String[] words, int k) {
         
             HashMap<String,Integer> map = new HashMap<>();
             //统计单词每个单词出现的顺序
             int value = 0;
             for(int i = 0;i < words.length;i++){
                 if(!map.containsKey(words[i])){
                     map.put(words[i],1);
                 }else{
                     value = map.get(words[i])+1;
                     map.put(words[i],value);
                 }
             }
             //自定义堆的排序规则:如果不相等,则按照value进行排序;如果相等,则按照字典顺序
             Queue<HashMap.Entry<String,Integer>> heap = new PriorityQueue<HashMap.Entry<String,Integer>>(new Comparator<HashMap.Entry<String,Integer>>(){
                 @Override
                 public int compare(HashMap.Entry<String,Integer> o1, HashMap.Entry<String,Integer> o2) {
                     if(o1.getValue().equals(o2.getValue())){
                         return o2.getKey().compareTo(o1.getKey());
                     }else{
                         return o1.getValue() - o2.getValue();
                     }
                 }
             });
            //遍历获取单词
             for (HashMap.Entry<String, Integer> entry : map.entrySet()) {
                 heap.offer(entry);
                 if (heap.size() > k) {
                     heap.poll();
                 }
             }
             //堆删除元素
             List<String> ret = new ArrayList<String>();
             while (!heap.isEmpty()) {
                 ret.add(heap.poll().getKey());
             }
             //按照删除顺序是由小到大了,所以需要反转
             Collections.reverse(ret);
             return ret;
         }
     }