LeetCode 初级 链表部分

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

  • 示例:
    • 给定一个链表: 1->2->3->4->5, 和 n = 2.
    • 当删除了倒数第二个节点后,链表变为 1->2->3->5.
  • 说明:
    • 给定的 n 保证是有效的。
  • 进阶:
    • 你能尝试使用一趟扫描实现吗?

解答:

  • 思路:
    • 方法: 快慢指针法
      • 快指针指向n个之后的元素,慢指针维持和快指针的间隔为n个元素
    • 边界条件:
      • 初始化的时候快指针就到nullptr了,此时 输入n等于链表的长度,相当于删除链表的第一个节点1
      • 循环的时候,快指针的next为nullptr,此时快指针指向末尾,慢指针指向 待删除节点的上一个节点。
        • 注意删除倒数第n个节点,和最后节点应该相差n-1个节点的,这里维持的间隔为n个节点,所以慢指针会指向待删除上上一个节点
  • Code
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* removeNthFromEnd(ListNode* head, int n) {
            ListNode* first=head;
            while(n--!=0) {first=first->next;}
            if(!first) {return head->next;} // 表示删除第一个
            ListNode* sec=head;
            while(first->next!=NULL){
                sec=sec->next;
                first=first->next;
            }
            sec->next=sec->next->next;
            return head;
        }
    };
    

反转链表

  • 反转一个单链表。
  • 示例:
    • 输入: 1->2->3->4->5->NULL
    • 输出: 5->4->3->2->1->NULL
  • 进阶:
    • 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

解答

  • 思路1: 递归法
    • 意思阐述:
      • 原来: 1->2->3->NULL
      • 现有: 1->NULL->3->2; 1->2
      • 修改为: 1->next->next=1; 1->next=nullptr
        • 3->2->1->Null
    • 边界条件:
      • 推出条件: head == null || head->next==null
    • Code
    	public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    	}
    
  • 思路2: 迭代法
    • 意思阐述:
      • 新建一个prev==null
      • 然后从head到尾进行迭代,将prev插入到迭代值的next上
    • 边界条件:
      • 当cur==null的时候,此时迭代可以终止了
        • 不是cur->next==null,因为此时cur的val是有效的
      • 返回prev
    • Code
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            ListNode* prev = nullptr;
            ListNode* cur = head;
            while(cur!=nullptr){
                ListNode* nextTemp = cur->next;
                cur->next = prev;
                prev = cur;
                cur = nextTemp;
            }
            return prev;
        }
    };
    

回文链表

  • 请判断一个链表是否为回文链表。
  • 示例
    • 示例 1:
      • 输入: 1->2
      • 输出: false
    • 示例 2:
      • 输入: 1->2->2->1
      • 输出: true
  • 进阶:
    • 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解答

  • 思路:
    • 找到中点
    • 后半段逆序
    • 进行比较
  • 注意点:
    • 找中点的时候使用快慢指针,快指针走两步,慢指针走一步,然后随时判断快指针是否为nullptr
    • 逆序时 不停的把prev放在末尾即可

验证二叉搜索树

  • 给定一个二叉树,判断其是否是一个有效的二叉搜索树。
  • 假设一个二叉搜索树具有如下特征:
    • 节点的左子树只包含小于当前节点的数。
    • 节点的右子树只包含大于当前节点的数。
    • 所有左子树和右子树自身必须也是二叉搜索树。
  • 示例:
    • 示例 1:
      输入:
      		2
      	   / \
      	  1   3
      输出: true
      
    • 示例 2:
      输入:
      	    5
      	   / \
      	  1   4
      	     / \
      	    3   6
      输出: false
      
  • 解释: 输入为: [5,1,4,null,null,3,6]。 根节点的值为 5 ,但是其右子节点值为 4 。

解答:

  • 方法一 递归
    • 思路
      • 每次判断一个
      • 注意上下边界问题:
        • 左: low - root.left
        • 右: root.right - high
    • Code
      class Solution {
      public:
          bool validbst(TreeNode* root, long l, long r){
              if(!root) {return true;}
              if(root->val <= l) {return false;}
              if(root->val >= r) {return false;}
              bool left = validbst(root->left, l, root->val);
              bool right = validbst(root->right, root->val, r);
              bool cur=true;
              if(root->left){
                  cur &= root->val > root->left->val;
              }
              if(root->right){
                  cur &= root->val < root->right->val;
              }
              return left&& right && cur;
          }
          bool isValidBST(TreeNode* root) {
              return validbst(root, LONG_MIN, LONG_MAX);
          }
      };
      
  • 方法二: 迭代(DFS,深度优先搜索算法)
    • 思路:
      • 和递归法类似,为此上下限
      • 使用堆栈来维持边界
    • Code
      class Solution {
      public:
          stack<pair<TreeNode*, pair<long, long>>> s;
          void update(TreeNode* root, long low, long high){
              s.push({root, {low, high}});
          }
          
          bool validbst(TreeNode* root, long l, long r){
              update(root, l, r);
              while(not s.empty()){
                  TreeNode* t = s.top().first;
                  long low = s.top().second.first;
                  long high = s.top().second.second;
                  s.pop();
                  if(t==nullptr){continue;}
                  if(t->val <= low) {return false;}
                  if(t->val >= high) {return false;}
                  update(t->right, t->val, high);
                  update(t->left, low, t->val);
              }
              return true;
          }
          bool isValidBST(TreeNode* root) {
              return validbst(root, LONG_MIN, LONG_MAX);
          }
      };
      
  • 方法三: 中序遍历
  • 思路

  • Code
    bool isValidBST(TreeNode* root) {
            // return validbst(root, LONG_MIN, LONG_MAX);
            stack<TreeNode*> s;
            long order = LONG_MIN;
            while(true){
                if(s.empty() && root==nullptr){break;}
                while(root){
                    s.push(root);
                    root=root->left;
                }
                root = s.top();
                s.pop();
                if(root->val <= order) {return false;}
                order = root->val;
                root = root->right;
            }
            return true;
        }
    

对称二叉树

  • 给定一个二叉树,检查它是否是镜像对称的。
    • 例1,二叉树 [1,2,2,3,4,4,3] 是对称的。
          1
         / \
        2   2
       / \ / \
      3  4 4  3
      
    • 例2, [1,2,2,null,3,null,3] 则不是镜像对称的:
        1
       / \
      2   2
       \   \
       3    3
      
  • 说明: 如果你可以运用递归和迭代两种方法解决这个问题,会很加分。

解答

  • 方法一 递归
    • 思路:
      • 递归输入: leftright
      • 递归判断条件:
        • left==nullptr && right==nullptr left和right都是null,为true
        • left==nullptr || right==nullptr left和right其中有一个为null,为false
        • left->val != right->val
      • 循环递归:
        • 内容:
          • {left->left, right->right}
          • {left->right, right->left}
        • 关系: &&
    • Code:
      class Solution {
      public:
          bool Symmetric(TreeNode* left, TreeNode* right){ // 函数主题
              if(left==nullptr && right==nullptr){return true;}
              if(left==nullptr && right!=nullptr){return false;}
              if(right==nullptr && left!=nullptr){return false;}
              if(left->val != right->val){return false;}
              return Symmetric(left->left, right->right) && Symmetric(left->right, right->left);
          }
          
          bool isSymmetric(TreeNode* root) {
              // 递归
              if(root==nullptr) {return true;}
              return Symmetric(root->left, root->right);
          }
      };
      
  • 方法二: 迭代法
    • 思路:
      • 输出储存形式: stack<pair<TreeNode*, TreeNode*>>
      • 迭代体输入:{left, right}
      • 迭代判断条件:
        • left==nullptr && right==nullptr left和right都是null,为true
        • left==nullptr || right==nullptr left和right其中有一个为null,为false
        • left->val != right->val
      • 迭代提输出:
        • {left->left , right->right}
        • {left->right, right->left}
    • Code
          bool isSymmetric(TreeNode* root) {
              // 递归
              //if(root==nullptr) {return true;}
              //return Symmetric(root->left, root->right);
              // 迭代法
              if(root==nullptr) {return true;}
              stack<pair<TreeNode*, TreeNode*>> s;
              s.push({root->right, root->left});
              while(not s.empty()){
                  TreeNode* r = s.top().first;
                  TreeNode* l = s.top().second;
                  s.pop();
                  if(l==nullptr && r==nullptr){continue;}
                  if(l==nullptr || r==nullptr){return false;}
                  if(l->val != r->val){return false;}
                  
                  s.push({r->right, l->left});
                  s.push({r->left, l->right});
              }
              return true;
          }
      

二叉树的层次遍历

  • 给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
  • 例如:
    • 给定二叉树: [3,9,20,null,null,15,7],
          3
         / \
        9  20
          /  \
         15   7
      
    • 返回其层次遍历结果:
      [
        [3],
        [9,20],
        [15,7]
      ]
      

解答

  • 方法一: 递归
    • 思路:
      • 递归输入: queue<TreeNode*> s; // 保存了某一个层次的节点
      • 递归输出:
        • 定义在类中方便访问: vector<vector<int>> result;
        • 本函数无返回值
      • 递归主体:
        • 输入: s
        • 输出:
          • queue<TreeNode*> z; // 循环递归的内容
          • vector<int> result_cur; // 当前递归主体的结果
        • 逻辑
          • s 中的每以项为 m
            • m==nullptr; continue
            • m->left 和 m->right 添加至z
            • m->val 添加至于result_cur
      • 循环递归:
        • 递归条件: not z.empty()
        • 内容:递归主体中的z
        • 返回: 无
      • 结果返回:
    • Code
      class Solution {
      public:
          vector<vector<int>> result;
          void reclevelOrder(queue<TreeNode*> z) {
              result.push_back(vector<int>());
              queue<TreeNode*> k;
              while(not z.empty()){
                  TreeNode* m = z.front();
                  z.pop();
                  if(m==nullptr){continue;}
                  k.push(m->left);
                  k.push(m->right);
                  result.back().push_back(m->val);
              }
              if(not k.empty()){
                  reclevelOrder(k);
              }
              if(result.back().empty()){
                  result.pop_back();
              }
          }
          
          vector<vector<int>> levelOrder(TreeNode* root) {
              queue<TreeNode*> k;
              k.push(root);
              reclevelOrder(k);
              return result;
          }
      };
      
  • 方法二: 迭代
    • 思路
      • 输出储存形式:
        • queue<TreeNode*> s; // 某一个level的点
        • vector<int> levelResult; // 当前等级的
      • 迭代体输入:s
      • 迭代判断条件:
        • s中的每一个为m
        • m!=nullptr;
        • m->left 和 m->right 添加至s中
        • m->value添加至levelResult中
      • 迭代提输出:
        • s
    • Code
      vector<vector<int>> levelOrder(TreeNode* root) {
              queue<TreeNode*> k;
              vector<vector<int>> result;
              k.push(root);
              while(not k.empty()){
                  int qs = k.size();
                  vector<int> levelresult;
                  while(qs--){
                      TreeNode* t = k.front();
                      k.pop();
                      if(t==nullptr){continue;}
                      levelresult.push_back(t->val);
                      k.push(t->left);
                      k.push(t->right);
                  }
                  if(!levelresult.empty()){
                      result.push_back(levelresult);
                  }
              }
              
              return result;
          }
      

将有序数组转换为二叉搜索树

  • 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
  • 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
  • 示例:
    • 给定有序数组: [-10,-3,0,5,9],
    • 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
            0
           / \
         -3   9
         /   /
       -10  5
      

  • 方法一: 递归
    • 思路: 将列表从中间分开,不停的二分下去即可得到解
      • 迭代主体:
        • 输入:
          • 列表:vector<int>& nums
          • 下限ID: int left
          • 上限ID: int right
        • 输出:
          • curleft = 迭代(nums, mid, left)
          • curright = 迭代(nums, right, mid)
        • 逻辑:
          • 最底层条件: if(left>right) return nullptr
          • mid = ceil((left+right)/2)
    • Code
      TreeNode* sortedArrayToBST(vector<int>& nums) {
              return helper(nums, 0, nums.size()-1);
          }
          
          TreeNode* helper(vector<int>& nums, int l, int r) {
              if(l>r){return nullptr;}
              int mid = floor((l+r)/2);
              auto t = new TreeNode(nums[mid]);
              t->left = helper(nums,  l, mid-1);
              t->right = helper(nums, mid+1, r);
              return t;
          }
      
  • 方法二: 迭代法
    • 思路
      • 迭代函数输入:
        • vector<int> nums;
        • pair<int, int> match; // 上下限
      • 迭代主体:
        • 迭代储存形式:stack<pair<TreeNode**, pair<int, int>>> s;
          • s.first 表示当前迭代level应该储存的位置,这里是二级指针
          • s.second 表示当前迭代 的上下限
        • 迭代逻辑:
          • 取输入:
            • TreeNode* &LevelSave = *(s.top.first);
            • pair<int, int> LevelMatch= s.top.second;
          • 控制条件:
            • if(LevelMatch->first > LevelMatch->second) continue;
          • int mid = floor((LevelMatch.first+LevelMatch.second)/2);
          • 喂输出:
            • LevelSave=new TreeNode(nums[mid]);
            • s.push({&(LevelSave.left), {LevelMatch.first, mid-1}});
            • s.push({&(LevelSave.right), {mid+1, LevelMatch.second}});
    • Code
      TreeNode* iterhelper(vector<int>& nums, pair<int, int> match){
              TreeNode* result = nullptr;
              stack<pair<TreeNode**, pair<int, int>>> s;
              s.push({&result, match});
              
              while(not s.empty()){
                  pair<TreeNode**, pair<int, int>> m = s.top();
                  s.pop();
                  
                  TreeNode* &curnode = *(m.first);
                  pair<int, int> curmatch = m.second;
                  
                  if(curmatch.first > curmatch.second){continue;}
                  
                  int mid = floor((curmatch.first + curmatch.second)/2);
                  curnode = new TreeNode(nums[mid]);
                  
                  s.push({&(curnode->left), {curmatch.first, mid-1}});
                  s.push({&(curnode->right), {mid+1, curmatch.second}});
              }
              return result;
          }
      

  1. while(n–):先判断n是不是0。如果n不为0,n减1,执行循环体。n的变化过程:n-1 ~ 0; while(–n):n先减1,再判断n是不是0。如果n不为0,执行循环体。n的变化过程:n-1 ~ 1 ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值