Skip to content

Hanqing1996/Leetcode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

coding地址

空间复杂度

  • 不需要额外的空间是指的算法的空间复杂度为O(1)级别
    • int result=2 就是不需要额外的空间
    • int arr[n] 就是需要额外的空间
  • 原地修改数组
for(int i=1;i<nums.size();i++){
   if(nums[i]!=nums[i-1]){
       nums[count]=nums[i-1];
       nums[++count]=nums[i];
   }
}

memset

int [256];
memset(Hash,-1,sizeof(Hash));

最值

  • java
Integer.MAX_VALUE
Integer.MIN_VALUE
  • c++
INT_MIN
INT_MAX

数组

  • 数组初始化 1. 错误姿势
int a[3] = {1};

等价于

int a[3] = {1,0,0};

2. 正确姿势

int Hash[256];
memset(Hash,-1,sizeof(Hash));// 不要memset(Hash,-1,256);

矩阵

  • 定义一个r*c的矩阵
vector<vector<int>> res(r, vector<int>(c));
  • 每行和每列元素均按升序排序
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]

1. matrix[0][0]是最小元素,matrix[m-1][n-1]是最大元素
2. matrix[i][j]向左,向上构成的矩阵中,matrix[i][j]为最大元素
3. 小于14的元素全在14的左边及上面(一个以14为结尾的小矩阵+11,12)

异或:相同为0,不同为1

  1. 0 ^ a = a
  2. 0 ^ 1 = 1; 0 ^ 0 = 0; 1 ^ 1 = 0; 1 ^ 0 =1
  3. 交换律:a ^ b ^ c <=> a ^ c ^ b
int ans =11;
ans=ans ^ 12; // ans=7

排序

二分查找

双指针

贪心

动态规划

链表

  • 空指针异常
first.next报错 // first=null

first.next.next报错 // first.next.next=null
  • 范例
// 24. 两两交换链表中的节点
class Solution {
    public ListNode swapPairs(ListNode head) {

        
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        
        ListNode first=dummy;
        
        // first.next=null表示剩余0个节点;first.next.next=null表示剩余1个节点,这两种情况不做处理
        while(first.next!=null&&first.next.next!=null) 
        {
            // 更新second,nextTmp位置,保证second,nextTmp不为空
            ListNode second=first.next;
            ListNode nextTmp=second.next;
            
            // 节点交换
            second.next=nextTmp.next;// 注意nextTmp.next为null时交换也在进行
            first.next=nextTmp;
            nextTmp.next=second;
            
            // 更新first位置,first可能为空
            first=nextTmp.next;
        }
        head=dummy.next;
        return head;
    }
}

链表的递归

二叉树(递归)

  1. 只能从父节点到子节点/子树(437,):可以用两次遍历
  2. 这条路径可以经过也可以不经过根节点(124,687,543):将“求经过该根节点的,满足题目要求的路径”转化为“根节点的left单向路径+根节点的right单向路径=满足题目要求的路径”
  3. 注意两次遍历要求第二次遍历不能是有选择的,不确定的(比如337就不能用二次遍历)。而且两次遍历的效率是很低的,很暴力,没有优化。
path(root.left) 
...
path(root.right)

image

  1. 先序遍历(二叉树路径/最大深度):[1 2 4 8 9 5 10 11 3 6 7]
  2. 中序遍历(二叉搜索树中序遍历序列为升序数组):[8 4 9 2 10 5 11 1 6 3 7]
  3. 后序遍历(平衡二叉树):[8 9 4 10 11 5 2 6 7 3 1]
/**
 * boolean类型的递归函数
 */
private boolean preorder(TreeNode p, TreeNode q){
        if(p!=null&&q!=null)
        {
            if(p.val!=q.val)
                return false;
            else
                return preorder(p.left,q.left)&&preorder(p.right,q.right);
        }
        else if((p!=null&&q==null)||(p==null&&q!=null))
            return false;
        else
            return true;
    }
class Solution {
    
    public boolean bal=true;
    public boolean isBalanced(TreeNode root) {
        
        balance(root);
        
        return bal;
    }
    
    /**
     * 自下向上
     */
    private int balance(TreeNode p){
        
        if(p!=null&&bal){
            int left_depth=balance(p.left);
            int right_depth=balance(p.right);
            
            if(Math.abs(left_depth-right_depth)>1){
                bal=false;
                return 0; // 这里return什么根本不重要
            }
            else
                return Math.max(left_depth,right_depth)+1;
        }
        else
            return 0;
    }   
}
class Solution {
    
    // 返回类型为TreeNode的递归函数
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        
        if(t1!=null&&t2!=null){
            t1.val+=t2.val;
            
            t1.left=merge(t1.left,t2.left);
            t1.right=merge(t1.right,t2.right);

            return t1; // 这一句会多次执行
        }
        else if(t1==null&&t2!=null){
            return t2;
        }
        else
            return t1;
    }
    
}

二叉搜索树

class Solution {
    public TreeNode trimBST(TreeNode root, int L, int R) {
       
        // 边界
        if(root==null)
            return null;
        
        // 讨论root的取值,按照不同情形返回不同的值
        if(root.val>=L&&root.val<=R){
            root.left=trimBST(root.left,L,R);
            root.right=trimBST(root.right,L,R);
            return root;
        }else if(root.val<L){
            return trimBST(root.right,L,R);    
        }else{
            return trimBST(root.left,L,R);
            }
    }
}

执行出错:AddressSanitizer

树的DFS,BFS,回溯

关于二叉搜索树的遍历

  • 中序遍历:结果一定为升序序列,运用详见230. 二叉搜索树中第K小的元素
  • 前序遍历:结果序列的最后一个元素一定为树中最大元素
  • 后序遍历:结果序列的第一个元素一定为树中最小元素

DP

  • 不连续
if(A[i]==B[j]){
    dp[i][j]=dp[i-1][j-1]+1;
}
else{
    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}

比如

对于
[2,6,7,4,1,8],
[4,3,2,7,5,4]
不连续:有dp[1][2]=max(dp[0][2],dp[1][1])=1;则dp[2][3]=dp[1][2]+1=2
连续:dp[2][3]=dp[1][2]+1=0+1=1
  • 连续
if(A[i]==B[j]){
    dp[i][j]=dp[i-1][j-1]+1;
}
    1. 最长回文子串
class Solution {
public:
    string longestPalindrome(string s) {
        int size = s.size();
        if (size < 2) {
            return s;
        }
        bool dp[size][size];        
        int maxLen = 1, start = 0;
        
        // 这个双层遍历实现了查找原先字符串的所有子串,注意它做到了先判断dp[8][8],再判断dp[7][9]
        for (int i = 0; i < size; ++i) {
            dp[i][i] = true; //单个字符本身
            for (int j = 0; j < i; ++j) {
                if (s[i] == s[j] && (i - j == 1 || dp[j+1][i-1])) {
                    dp[j][i] = true;
                    if (i - j + 1 > maxLen) {
                        maxLen = i - j + 1;
                        start = j;
                    }
                } else {
                    dp[j][i] = false;
                }
            }
        }
        return s.substr(start, maxLen);
    }
};

树形dp

     3
    / \
   2   7
    \   \ 
     6   1
  1. 正确思路:看作(节点3+以2为根节点的左子树+以7为根节点的左子树 如果我们取3,那么左子树root.left 和 右子树root.right我们都不能取了;如果我们不取,那么最大值来自于左右子树的和,也就是3.left+3.right。但是对于左右子树来说,又涉及到了上面的问题,我们是取还是不取呢?这样循环递推下去,直到节点为null,我们直接返回0即可。那么我们对于每个节点,都要设计一个结构来描述取和不取这样的操作
  2. 错误思路:看作(节点3+节点2+节点7) 如果3取,则2,7不能取。然后呢?6,7要不要取呢?感觉好复杂
class Solution {
    public int rob(TreeNode root) {
    
        // 一开始是可以偷的,所以flag=1
        return robCurrentNode(1, root);
    }
    public int robCurrentNode(int flag, TreeNode root) {
        if (root == null) return 0;

        // 如果当前节点可以偷,return 偷左右子树的情况下.max(不偷当前节点,偷当前节点)
        if (flag == 1) return Math.max(robCurrentNode(1, root.left)+robCurrentNode(1, root.right), robCurrentNode(0, root.left)+robCurrentNode(0, root.right)+root.val);

        // 如果当前节点不可以偷,return  偷左右子树.不偷当前节点
        else return robCurrentNode(1, root.left)+robCurrentNode(1, root.right);
    }
}

字符串

  • 由"dog cat cat dog"得到["dog","cat","cat","dog"]
int len=str.size();
int pos=0;

vector<string> words;

while(pos!=-1){
   pos=str.find_first_of(' ');
   words.push_back(str.substr(0,pos));
   str=str.substr(pos+1,len-pos-1);
} 

Hash

int main() {
	int Hash[256];
	string str="aaa";
	for(int i=0;i<3;i++){
	Hash[str[i]]++;
	}
	cout<<Hash['a']; // 3
	
	string str2="AAA";
	for(int i=0;i<3;i++){
	Hash[str2[i]]++;
	}
	cout<<Hash['a']; // 2326231
}
class Solution {
public:
    int numJewelsInStones(string J, string S) {
        int Hash[58]={0};
        int count=0;
        for(int i=0;i<J.size();i++){
            Hash[J[i]-'A']++;
        }
        for(int i=0;i<S.size();i++){
            if(Hash[S[i]-'A']==1)
                count++;
        }
        return count;
    }
};

观察规律

回溯

    void DFS(int x){
        
        // 到达叶节点
        if(x==len){
            cnt++;
            
            // 如果是第k个排列
            if(cnt==seq){
                
                for(int i=0;i<len;i++){
                    res+=a[i]+'0';
                }
            }
        }
        for(int i=0;i<len;i++){
            if(vest[i]==false){
                a[x]=nums[i];
                vest[i]=true;
                DFS(x+1);
                vest[i]=false;
            }
        }
        
    }

背包

背包结果一定保证index有序,即不会得到 nums[8] nums[4] nums[1] nums[10] 这种结果

    void DFS(int index,int cnt){
    
        // 到达叶节点
        if(index==maxSize){
            
	    // 一定要写在if(index==maxSize)里面
            if(cnt==targetNum){
                temp.clear();
                for(int i=0;i<cnt;i++){
                    temp.push_back(a[i]);
                }
                res.push_back(temp);
            }
            return;
        }

        // 选
        if(cnt<=targetNum-1){
            a[cnt]=nums[index];
            DFS(index+1,cnt+1);
        }
        
        // 不选
        DFS(index+1,cnt);
    }

DFS全排列

关于指针

  • 指针的用法
#include<iostream>
using namespace std;

贪心

About

刷起来,别停!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published