剑指offer - 总

07.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //递归分治
        return recursionBuild(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
    }

    //递归分治
    TreeNode* recursionBuild(vector<int>::iterator preBegin, vector<int>::iterator preEnd,vector<int>::iterator inBegin, vector<int>::iterator inEnd )
    {
        if(inEnd==inBegin) return NULL;
        TreeNode* cur = new TreeNode(*preBegin);
        auto root = find(inBegin,inEnd,*preBegin);
        cur->left = recursionBuild(preBegin+1,preBegin+1+(root-inBegin),inBegin,root);
        cur->right = recursionBuild(preBegin+1+(root-inBegin),preEnd,root+1,inEnd);
        return cur;
    }
};

感想:迭代器迭代器迭代器

26.树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A==NULL|| B==NULL) return false;
        bool res = false;
        if(A->val == B->val){
            res = helper(A,B);
        }
        if(!res){
            res = isSubStructure(A->left,B);
        }
        if(!res){
            res = isSubStructure(A->right,B);
        }
        return res;
    }
    
    bool helper(TreeNode* a,TreeNode* b){
        if(b==NULL) return true;
        if(a==NULL) return false;
        if(a->val == b->val) return helper(a->left,b->left) && helper(a->right,b->right);
        else return false;
    }
};

感想:递归敢于截止。

28. 对称的二叉树

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        else  return helper(root->left,root->right);
    }

    bool helper(TreeNode* leftRoot,TreeNode* rightRoot){
        if(leftRoot == NULL && rightRoot == NULL) return true;
        else if(leftRoot == NULL || rightRoot == NULL) return false;
        if(leftRoot->val != rightRoot->val) return false;
        else return helper(leftRoot->left,rightRoot->right) && helper(leftRoot->right,rightRoot->left);
    }
};

55.二叉树深度

递归和非递归两种写法

/**************************BFS*************************/

class Solution {
public:
    int length=0;
    queue<TreeNode*> q;
    int maxDepth(TreeNode* root) {
       if(root == NULL) return 0;
       q.push(root);
       while(!q.empty()){
           int size = q.size();
           for(int i=0;i<size;i++){
               TreeNode* tmp = q.front();q.pop(); 
    //注意这个出队操作是写在横排for的里面的,就是说同一行的节点的出队操作值归为length加一次。
               if(tmp->left) q.push(tmp->left);
               if(tmp->right) q.push(tmp->right);
           }
           length+=1;
       }
       return length;
    }
};

/****************DFS**************************/
//递归
class Solution {
public:
    int maxDepth(TreeNode* root) {
	    if(root == NULL) return 0;
	    return max(maxDepth(root->left),maxDepth(root->right))+1;
    }
};

32-3 从上到下打印二叉树

class Solution {
public:
    vector<vector<int>> v;
    vector<vector<int>> levelOrder(TreeNode* root) {
        dfs(root,0);
        return v;
    }

    void dfs(TreeNode* root,int level){
        if(root == NULL) return ;
        if(level>=v.size()) v.emplace_back(vector<int>());
        v[level].emplace_back(root->val);
        if(root->left) dfs(root->left,level+1);
        if(root->right) dfs(root->right,level+1);
    }
};

33.二叉搜索树树后序遍历序列

在这里插入图片描述

bool fun2(vector<int> &a,int left,int right){
	if(left >= right) return true;
	int pos = left;
	for(;pos<right;pos++){
		if(a[pos]>a[right]) break;
	}
	for(int j=pos;j<right;j++){
		if(a[j]<a[right]) return false;
	}
	return fun2(a,left,pos-1) && fun2(a,pos,right-1);
}

void fun1(vector<int> &a){
	fun2(a,0,a.size()-1);	
} 

34.二叉树中和为某一值的路径

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

 

示例:
给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1

返回:

[
   [5,4,11,2],
   [5,8,4,5]
]
class Solution {
public:
    vector<vector<int>> v;
    vector<int> single;
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if(root == NULL) return v;
        dfs(root,sum);
        return v;
    }

    void dfs(TreeNode* root,int sum){
        if(root == NULL) return;
        sum -= root->val;
        single.push_back(root->val);
        if(!root->left && !root->right && sum == 0) v.emplace_back(single);
        dfs(root->left,sum);
        dfs(root->right,sum);
        single.pop_back();
    }
};

感想:想成了每次怎么新建一个vector实际上每一条dfs深度里都是一条新的vector,即使用的全局变量。

68-1 二叉搜索树中的最近祖先

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root->val < p->val && root->val < q->val) return lowestCommonAncestor(root->right, p, q);
        if(root->val > p->val && root->val > q->val) return lowestCommonAncestor(root->left, p, q);
        return root;
    }
};

68-2二叉树中的最近公共祖先

在这里插入图片描述

TreeNode* fun1(TreeNode *root,TreeNode *p,TreeNode *q){
	if(root == NULL) return NULL;
	if(root->val == p->val || root->val == q->val) return root;
	TreeNode *left = fun1(root->left,p,q);
	TreeNode *right = fun1(root->right,p,q);
	if(left == NULL && right == NULL) return NULL;
	if(left == NULL) return right;
	if(right == NULL) return left;
	return root;
}

06.从尾到头打印链表

利用vector的从头插入功能:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> c;
        if(head == NULL){
            return c;
        }
        while(head->next!=NULL){
            c.insert(c.begin(),head->val);
            head = head->next;
        }
        c.insert(c.begin(),head->val);
        return c;
    }
};

还可以push_back加reverse:

reverse反转法

        while(head){
            res.push_back(head->val);
            head = head->next;
        }
        //使用algorithm算法中的reverse反转res
        reverse(res.begin(),res.end());
        return res;

18.删除指定节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        ListNode* p = head;
        ListNode* q = p;
        if(p->val == val){
            head = head->next;
            return head;
        }
        while(p->next){
            q=p;
            p=p->next;
            if(p->val == val){
                if(p->next) q->next = p->next;
                else q->next = NULL;
                break;
            }
        }
        return head;
    }
};

24.翻转链表

感想:重点在怎么去新建一个指针类型的节点并把指针指向这个新的节点。

/**
 * 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) {
        if(head == NULL || head->next == NULL) return head;
        ListNode* tail = head;
        stack<int> s;
        while(tail->next){
            s.push(tail->val);
            tail = tail->next;
        }
        cout<<s.size()<<endl;
        cout<<tail->val<<endl;
        ListNode* p =tail;
        while(!s.empty()){
            ListNode* node = new ListNode(s.top());
            s.pop();
            p->next = node;
            p = p->next;
        }
        return tail;
    }
};

25.合并两个排序的链表

感想:最好的方法不是将L2插入l1,而是新建头结点,谁大往头结点后面续。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL) return l2;
        else if(l2 ==NULL) return l1;
        ListNode* p;
        if(l1->val<l2->val) {
            p = l1;
            l1 = l1->next;
        }else{
            p = l2;
            l2 = l2->next;
        } 
        ListNode* head = p;
        while(l1 && l2){
            cout<<"l1-l2 "<<l1->val<<" "<<l2->val<<endl;
            if(l1->val < l2->val){
                p->next = l1;
                cout<<"l1 big"<<p->val<<endl;
                p = p->next;
                cout<<"l1 big"<<p->val<<endl;
                l1 = l1->next;
                cout<<"l1 big"<<p->val<<endl;
            }else if(l1->val == l2->val){
                p->next = l1;
                p = p->next;
                l1 = l1->next;
                p->next = l2;
                p = p->next;
                l2 = l2->next;
                cout<<p->val<<"="<<endl;
            }else{
                p->next = l2;
                p = p->next;
                l2 = l2->next;
                cout<<"l2 big"<<p->val<<endl;
            }
        }
        if(l1) p->next = l1;
        if(l2) p->next = l2;
        return head;
    }
};

47.礼物最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

 

示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

感想:改造原有矩阵节省空间且易于理解,但边界初始化要细心:

class Solution {
public:
    int weight = 0;
    int m,n;
    int maxValue(vector<vector<int>>& grid) {
        m = grid.size()-1;
        n = grid[m].size()-1;
        if(m==0 && n == 0) return grid[m][n]; 
        //边界初始化要细心
        for(int i=0;i<=m;i++){
            for(int j=0;j<=n;j++){
                if(i == 0 && j == 0) continue;
                //边界初始化要细心
                if(i == 0) grid[i][j] = grid[i][j-1]+grid[i][j]; 
                //边界初始化要细心
                else if(j == 0) grid[i][j] = grid[i-1][j]+grid[i][j];
                else grid[i][j] = max(grid[i-1][j],grid[i][j-1])+grid[i][j];
                cout<<"i "<<i<<" -- j "<<j<<"="<<grid[i][j]<<endl;
            }
        }
        return grid[m][n];
    }
};

42.连续子序列的和同LC53

初始化很重要。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int size = nums.size();
        if(size == 0) return 0;
        vector<int> dp(size,0);
        dp[0] = nums[0];
        int result = nums[0];
        for(int i=1;i<size;i++){
            dp[i]=dp[i-1]>0?nums[i]+dp[i-1]:nums[i];
            if(dp[i]>result) result = dp[i];
        }
        return result;
    }
};

LC53

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];
        int max=nums[0];
        for(int i=1;i<nums.size();i++){
            nums[i]=nums[i-1]>0?nums[i-1]+nums[i]:nums[i];
            if(nums[i]>max) max = nums[i];
        }
        return max;
    }
};
40.最小的k个数

快速排序:

参考链接:https://blog.csdn.net/nrsc272420199/article/details/82587933

package com.nrsc.sort;

public class QuickSort {
	public static void main(String[] args) {
		int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
		quickSort(arr, 0, arr.length - 1);
		System.out.println("排序后:");
		for (int i : arr) {
			System.out.println(i);
		}
	}

	private static void quickSort(int[] arr, int low, int high) {

		if (low < high) {
			// 找寻基准数据的正确索引
			int index = getIndex(arr, low, high);

			// 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
			quickSort(arr, 0, index - 1);
			quickSort(arr, index + 1, high);
		}

	}

	private static int getIndex(int[] arr, int low, int high) {
		// 基准数据
		int tmp = arr[low];
		while (low < high) {
			// 当队尾的元素大于等于基准数据时,向前挪动high指针
			while (low < high && arr[high] >= tmp) {
				high--;
			}
			// 如果队尾元素小于tmp了,需要将其赋值给low
			arr[low] = arr[high];
			// 当队首元素小于等于tmp时,向前挪动low指针
			while (low < high && arr[low] <= tmp) {
				low++;
			}
			// 当队首元素大于tmp时,需要将其赋值给high
			arr[high] = arr[low];

		}
		// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
		// 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
		arr[low] = tmp;
		return low; // 返回tmp的正确位置
	}
}


91.编码

在这里插入图片描述
注意,字符串是从第二个字符串开始审查的,但是dp数组需要用到dp[i-2]和dp[i-1]所以要先初始化dp[0]和dp[1],因此dp要比s往后多一个数字:

class Solution {
public:
    int numDecodings(string s) {
        if(s.size() == 0 ||s[0]=='0') return 0;
        vector<int> dp(s.size()+1);
        dp[0]=1;dp[1] = 1;
        for(int i=1;i<s.size();i++){
            if(s[i] == '0'){
                if(s[i-1]=='1'||s[i-1]=='2'){
                    dp[i+1] = dp[i-1];
                    cout<<"here1"<<endl;
                }
                else return 0;
            }else{
                int temp=(s[i-1]-'0')*10+(s[i]-'0');
                if(temp>=10 && temp<=26) {
                    cout<<"here2"<<endl;
                    dp[i+1] = dp[i]+dp[i-1];
                }
                else {
                    cout<<"here3"<<endl;
                    dp[i+1] = dp[i];
                }
            }
        }
        return dp[s.size()];
    }
};

46.全排列

在这里插入图片描述

class Solution {
public:

    int len = 0;
    void fun(vector<vector<int>> &s,vector<int> &single,int first){
        if(first == len){
            s.emplace_back(single);
            return ;
        }
        for(int i=first;i<len;i++){
            swap(single[first],single[i]);
            fun(s,single,first+1);
            swap(single[first],single[i]);
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        len = nums.size();
        vector<vector<int>> s;
        fun(s,nums,0);
        return s;
    }
};

415.大数相加

假装位数一样不够的前面补零

class Solution {
public:
    string addStrings(string num1, string num2) {
        int l1=num1.length()-1,l2=num2.length()-1;
        int carry=0;
        string s="";
        while(carry || l1>=0 || l2>=0){
            int x = l1<0?0:num1[l1--]-'0';
            int y = l2<0?0:num2[l2--]-'0';
            s.insert(0,1,char((x+y+carry)%10)+'0');
            carry=(x+y+carry)/10;
        }
        return s;
    }
};

03.数组中的重复数字

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1
的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3

  1. 二重循环 失败原因:超时
  2. hashset 失败原因:只有java有hashset,c++没有这个数据结构
class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        int repeat = -1;
        for (int num : nums) {
            if (!set.add(num)) {
                repeat = num;
                break;
            }
        }
        return repeat;
    }
}
  1. bool数组将原数组作为索引 成功
class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
      bool b[nums.size()];
      memset(b,false,sizeof(b));
      for(int i=0;i<nums.size();i++){
          if(b[nums[i]]==false){
              b[nums[i]] = true;
          }else{
              cout<<nums[i]<<endl;
              return nums[i];
          }
      }
      return -1;
    }
};

21. 调整数组顺序使奇数位于偶数前面

两种很好的方法:首尾双指针和快慢双指针
在这里插入图片描述

在这里插入图片描述

//首尾双指针
vector<int> fun1(vector<int>& nums){
	int l = 0,r = nums.size()-1;
	while(l<r){
		if(nums[l] %2 ==1){
			l++;
			continue;
		}
		if(nums[r] %2 ==0){
			r--;
			continue;
		}
		swap(nums[l],nums[r]);
	}
	for(int i=0;i<nums.size();i++){
		cout<<nums[i]<<" ";
	}
	return nums;
}

在这里插入图片描述

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

//快慢双指针
vector<int> fun2(vector<int>& nums){
		int low=0,fast=0;
		while(fast < nums.size()){
			if(nums[fast] % 2 == 1){
				swap(nums[low],nums[fast]);
				low++;
			}
			fast++;
		}
	for(int i=0;i<nums.size();i++){
		cout<<nums[i]<<" ";
	}
	return nums;
}

int main(){
	vector<int> nums{1,3,4,5,6,7,2,9,8};
	fun1(nums);
	cout<<endl;
	fun2(nums);
}

30.包含min函数的栈

在这里插入图片描述
在这里插入图片描述

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> a,b;
    MinStack() {
        
    }
    
    void push(int x) {
        a.push(x);
        if(b.empty() || b.top() <= x) b.push(x);
    }
    
    void pop() {
        int x = a.top();a.pop();
        if(b.top() == x) b.pop();
    }
    
    int top() {
        return a.top();
    }
    
    int min() {
        return b.top();
    }
};

31. 栈的压入弹出序列

在这里插入图片描述

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> s;
        int j=0;
        cout<<pushed.size()<<endl;
        for(int i=0;i<pushed.size();i++) {
            s.push(pushed[i]); //先放进去再说
            while(!s.empty() && s.top() == popped[j]){
                s.pop();
                j++;
            }
        }
        return s.empty();
    }
};

04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
  [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]
]

给定 target = 5,返回 true。

给定 target = 20,返回 false。
  1. 先查找左边界,再锁定到具体行,行内排查。 失败:并不是下一行开头就比上一行结尾大,题意理解不当。
  2. 找规律:
    在这里插入图片描述note:必须要有判断矩阵是否为空的判断,否则无法执行。
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
         if (matrix.size() == 0){
            return false;
        }
        int n =matrix.size();
        int m = matrix[0].size();
        int i=n-1;
        int j=0;
        while(i>=0 && j <m){
            if(target == matrix[i][j]){
                return true;
            }else if(target < matrix[i][j]){
                i--;
               //cout<<matrix[i][j]<<" i--";
            }else{
                j++;
                //cout<<matrix[i][j]<<" j++";
            }
        }
        return false;
    }
};
  1. 递归
    在 midmidmid 列寻找满足条件
 matrix[row − 1][mid] < target < matrix[row][mid]

的点,比如当 row=3,mid=2时(黄色区域),9<target<14,这时我们可以判断出来 target 一定在左下或者右上区域:

由 target>9,可知 target在 9 的右侧或下侧;
由 target<14,可知 target在 14的上侧或左侧;

因此对左下和右上两个区域进行递归,直到遇到终止条件进行回溯,返回结果。 终止条件为:

区域中没有元素;
target大于深色区域右下角的值(最大值)或小于深色区域左上角的值(最小值)

其中,找到黄色点的方法如下:

列索引 mid采用二分查找;
行索引沿 mid 列从上向下移动,并保持该位置元素小于 target。

在这里插入图片描述### 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

没有开辟额外空间,先根据空格数量在字符串末尾扩容两个字符的空间(因为一个空格变为%20需要多出两个空间),
然后倒叙遍历将原来位置的字符放到后面, 最后返回s就可以了.

class Solution {
public:
    string replaceSpace(string s) {
       int l1 = s.length()-1;
        for (int i = 0; i <= l1; i++) {
            if (s[i] == ' ') {
                s += "00";
            }
        }
        int l2 = s.length() - 1;
        if (l2 <= l1) {
            return s;
        }
        for (int i = l1; i >= 0; i--) {
            char c = s[i];
            if (c == ' ') {
                s[l2--] = '0';
                s[l2--] = '2';
                s[l2--] = '%';
            } else {
                s[l2--] = c;
            }
        }
        return s;

    }
};

48.旋转图像(矩阵)

在这里插入图片描述

采用分层来进行平移的方式,将矩阵的每一层都分开进行旋转,比如5*5的矩阵可以分为3层
矩阵分层.png
旋转的时候,每四个矩阵块作为一组进行相应的旋转
图片.png
image.png
可以看出,第二次旋转的时候比第一次旋转偏移了一格,这里我们使用add变量来记录矩阵块的偏移量,首先不考虑偏移量的时候写出左上角的坐标为(pos1,pos1),右上角的坐标为(pos1,pos2),左下角的坐标为(pos2,pos1),右下角的坐标为(pos2,pos2),则能够写出偏移之后对应的坐标
坐标变换.png
每次计算完一层之后,矩阵向内收缩一层,
矩阵向内收缩图.png
所以有pos1 = pos1+1,pos2 = pos2-1,终止的条件为pos1 < pos2

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int p1=0,p2=matrix.size()-1;
        while(p1<p2){ //每一层
            int add=0;
            while(add<p2-p1){ 
                int tmp=matrix[p1+add][p2];
                matrix[p1+add][p2] = matrix[p1][p1+add];
                matrix[p1][p1+add] = matrix[p2-add][p1];
                matrix[p2-add][p1] = matrix[p2][p2-add];
                matrix[p2][p2-add] = tmp;
                add +=1;  //每一层中每个点的迁移
            }
            p1+=1; //收缩层
            p2-=1;
        }
    }
};

12.矩阵中的路径(dfs)

在这里插入图片描述

dfs+剪枝

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        rows = board.size();
        cols = board[0].size();
        for(int i = 0; i < rows; i++) {
            for(int j = 0; j < cols; j++) {
                if(dfs(board, word, i, j, 0)) return true;
            }
        }
        return false;
    }
private:
    int rows, cols;
    bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
        if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false; //不是起点或者越界
        if(k == word.size() - 1) return true;
        board[i][j] = '\0';
        bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = word[k];
        return res;
    }
};

29.顺时针旋转矩阵

在这里插入图片描述

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& m) {
        vector<int> s;
        if(m.size() == 0) return s;
        int l=0,r=m[0].size()-1,t=0,b=m.size()-1,x=0;
        
        while(true){
            for(int i = l; i <= r; i++) s.push_back(m[t][i]);
            if(++t  > b) break;
            for(int i = t; i <= b; i++) s.push_back(m[i][r]);
            if(--r < l) break;
            for(int i = r; i >= l; i--) s.push_back(m[b][i]);
            if(--b < t) break;
            for(int i = b; i >= t; i--) s.push_back(m[i][l]);
            if(++l > r) break;
        }
        for(vector<int>::iterator it = s.begin(); it != s.end(); it++){
            cout<<*it<<" ";
        }
        return s;
    }
};

56-1.数组中数字出现的次数

位异或,不要急拿笔演算一下试一试,但是只适用于双数出现中挑选出单数出现的,局限性比较大
在这里插入图片描述

public int[] singleNumbers(int[] nums) {
        //xor用来计算nums的异或和
        int xor = 0;

        // 计算异或和 并存到xor
        // e.g. [2,4,2,3,3,6] 异或和:(2^2)^(3^3)^(4^6)=2=010
        for(int num : nums) xor ^= num;

        //设置mask为1,则二进制为0001
        // mask是一个二进制数,且其中只有一位是1,其他位全是0,比如000010,
        // 表示我们用倒数第二位作为分组标准,倒数第二位是0的数字分到一组,倒数第二位是1的分到另一组
        int mask = 1;

        // & operator只有1&1时等于1 其余等于0
        // 用上面的e.g. 4和6的二进制是不同的 我们从右到左找到第一个不同的位就可以分组 4=0100 6=0110
        // 根据e.g. 010 & 001 = 000 = 0则 mask=010
        // 010 & 010 != 0 所以mask=010
        // 之后就可以用mask来将数组里的两个数分区分开
        while((xor & mask)==0){
            mask <<= 1;
        }

        //两个只出现一次的数字
        int a=0, b=0;

        for(int num : nums){
            //根据&是否为0区分将两个数字分区,并分别求异或和
            if((num & mask)==0) a ^= num;
            else{
                b ^= num;
            }
        }
        return new int[]{a,b};
    }

56-2

map的写法,比较倾向于是一个通法。
在这里插入图片描述

class Solution {
public:
    int singleNumber(vector<int>& nums) {
    	//哈希表方法
        map<int,int> mp;
	    for(int i=0;i<nums.size();i++){
		    mp[nums[i]]++;
		    cout<<nums[i]<<":"<<mp[nums[i]]<<endl;
        }
        int ans;
        for(map<int,int>::iterator it = mp.begin();it!=mp.end();it++){
            if(it->second == 1){
                ans = it->first;
                cout<<ans<<endl;
                break;
            }
        }
        return ans; 
    }
};

方法二:位运算

  1. 值得注意的是:如果某个数字出现3次,那么这个3个数字的和肯定能被3整除,则其对应二进制位的每一位的和也能被3整除
  2. 统计数组中每个数字的二进制中每一位的和,判断该和是否能被3整除。
  3. 若可以,则只出现一次的数字的二进制数中那一位为0,否则为1
    话不多说看不懂的话没关系,推演一下就明白了,这个方法也可以作为通式通法
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        //位运算方法
        int ans = 0;
        for(int i=0;i<32;i++){
            int cnt = 0;
            for(int j=0;j<nums.size();j++){
                if(nums[j] & (1<<i)) cnt++;
            }
            if(cnt % 3 == 1) ans ^= (1<<i);
        } 
        cout<<ans;
        return ans;
    }
};
162.递归迭代二分寻找峰值

算法题:给定一个数组,求峰值(比左边一个大,比右边一个大),暴力O(n)很显然,但是有logn的。我想到了一部分,如果两边都不是峰值,那么峰值一定在中间。这是可以二分的。但是我没敢说,因为太紧张了我无法证明。归根结底是我对二分理解得不够深入,之前遇到的二分都是排好序的数组的二分,这次一个没排序的也可以二分让我大开眼界。本题凉
在这里插入图片描述
普通线性查找:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

/***********************************递归二分查找*******************************************/
int helper(int *a,int l,int r){
	if(l == r) return l;
	int mid = (l + r)/2;
	if(a[mid] > a[mid+1]) return helper(a,l,mid);
	return helper(a,mid+1,r);
}

int peak1(int *a, int len){
	return helper(a,0,len-1);
}
/***********************************递归二分查找*******************************************/

/***********************************迭代二分查找*******************************************/
int peak2(int *a, int l, int r){
	while(l < r){
		int mid = (l+r)/2;
		if(a[mid]> a[mid+1]) r = mid;
		else l = mid+1;
	}
	return r;
}
/***********************************迭代二分查找*******************************************/
int main(){
	int a[]= {1};
	int len = sizeof(a)/sizeof(a[0]);
	cout << peak1(a, len)<<endl;
	cout << peak2(a, 0, len-1)<<endl;
}

在这里插入图片描述

103.蛇形二叉树遍历

算法题:给定一棵二叉树,返回蛇形遍历顺序。leetcode水题,宽度优先遍历之后将偶数行的结果reverse就可以。只做出这一个。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

struct TreeNode{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x): val(x), left(NULL), right(NULL){};
};

TreeNode* init(int *a, int len){
	TreeNode *root = nullptr;
	TreeNode *p = nullptr;
	int flag = 0;
	for(int i=0;i<len;){
		if(i == 0)  {
				root = new TreeNode(a[i++]);
				p = root;
				cout<<p->val<<"mmm"<<endl;
		}else{
				cout<<a[i]<<"mmm"<<endl;
				if(a[i] == NULL) p->left=nullptr;
				else p->left = new TreeNode(a[i]);
				i++;
				cout<<a[i]<<"mmm"<<endl;
				if(a[i] == NULL) p->right=nullptr;
				else p->right = new TreeNode(a[i]);
				i++;
				if(flag == 0) {
						if(p->left) p = p->left;
						flag=1;
				}else if(flag == 1){
					if(p->right) p = p->right;
					flag = 0;
				}
		}
	}
	return root;
}


void levelOrder(TreeNode *root){
	TreeNode *p = root;
	queue<TreeNode*> q;
	q.push(root);
	vector<vector<int>> v;
	int level = 1;
	while(!q.empty()){
		int size = q.size();
		vector<int>t;
		for(int i=0;i<size;i++){
			p = q.front();q.pop();
			t.push_back(p->val);
			if(p->left) q.push(p->left);
			if(p->right) q.push(p->right);
		}
		if(level %2 == 0) reverse(t.begin(),t.end());
		v.push_back(t);
		level++;
	}
	for(int i=0;i<v.size();i++){
		for(vector<int>::iterator it = v[i].begin(); it != v[i].end(); it++){
			cout<<*it<<",";
		}
		cout<<endl;
	}
}

int main(){
	int a[] = {3,9,20,NULL,NULL,15,7};
	int len = sizeof(a)/sizeof(a[0]);
	TreeNode *root = init(a, len);
	levelOrder(root);

}

84. 柱状图中最大的矩形(单调栈/暴力)

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

int fun(vector<int>& heights){
	stack<int> s;
	int size = heights.size();
	int res = 0;
	for(int i=0;i<size;i++){
		while(!s.empty() && heights[s.top()] > heights[i]){
			int length = heights[s.top()];
			s.pop();
			int weight = i;
			if(!s.empty()){
				weight = i - s.top() - 1;
			}
			res = max(res, length*weight);
		}
		s.push(i);
	}

	while(!s.empty()){
		int length = heights[s.top()];
		s.pop();
		int weight = size;
		if(!s.empty()){
			weight = size - s.top() -1;
		}
		res = max(res, length*weight);
	}
	return res;
}

int main(){
	vector<int> heights={2, 1, 5, 6, 2, 3};
	cout<<fun(heights)<<endl;
}

单调栈

https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/

42.接雨水

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

int fun(int height[],int len){
	stack<int> s;
	int res = 0;
	//cout<<len<<endl;
	for(int i=0;i<len;i++){
		//cout<<i<<" "<<endl;
		while(!s.empty() && height[i] > height[s.top()]){
			int bottom = height[s.top()];s.pop();
			if(s.empty()){
				break;
			}
			int weight = i - s.top() - 1;
			int top= min(height[i],height[s.top()]);
			res = res + (weight*(top-bottom));
			cout<<"i="<<i<<" res="<<res<<endl;
		}
		s.push(i);
	}
	cout<<res<<" ";
	return res;
}

int main(){
	int a[] = {0,1,0,2,1,0,1,3,2,1,2,1};
	int len = sizeof(a)/sizeof(a[0]);
	fun(a,len);
} 
第k大的数

https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/3chong-jie-fa-miao-sha-topkkuai-pai-dui-er-cha-sou/#%E4%B8%89%E3%80%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B9%9F%E5%8F%AF%E4%BB%A5-%E8%A7%A3%E5%86%B3-topk-%E9%97%AE%E9%A2%98%E5%93%A6

题目链接 https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/submissions/
在这里插入图片描述

堆排

class Solution {
public:
    void adjustHeap(vector<int>& a,int i,int len){
        int tmp = a[i];
        for(int m=2*i+1;m<len;m=m*2+1){
            if(m+1<len && a[m+1] > a[m]) m++;
            if(a[m] > tmp){
                a[i] = a[m];
                i = m;
            }
        }
        a[i] = tmp;
    }

    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        for(int i=k/2-1;i>=0;i--){
            adjustHeap(arr,i,k);
        }
        for(int i=k;i<arr.size();i++){
            if(arr[i] <= arr[0]){
                swap(arr[i],arr[0]);
                adjustHeap(arr,0,k);
            }
        }
        vector<int> v;
        for(int i=0;i<k;i++){
            v.emplace_back(arr[i]);
        }
        return v; //返回第k个数则返回a[0],而不是a[k];
    }
};

快排

#include<bits/stdc++.h>
using namespace std;

void print(vector<int>& arr){
    for(int i=0;i<arr.size();i++)
    cout<<arr[i]<<" ";
    cout<<endl;
}
int getIndex(vector<int>& arr,int low,int high){
        int tmp = arr[low];
        while(low < high){
            while(low < high && arr[high] >= tmp){
                high--;
            }
            arr[low] = arr[high];
            while(low < high && arr[low] <= tmp){
                low++;
            }
            arr[high] = arr[low];
        }
        arr[low] = tmp;
        return low;
    }

void QuickSort(vector<int>& arr, int low,int high,int k){
    if(low <high){
            int index = getIndex(arr,low,high);
            cout<<"index="<<index<<endl;
            print(arr);
            if(index == k){
                cout<<"get"<<endl;
                return;
            }else if(index > k){ //这里不是Index<k不要写反了
                cout<<"here low="<<low<<endl;
                QuickSort(arr,low,index-1,k);
            }else{
                cout<<"here high="<<high<<endl;
                QuickSort(arr,index+1,high,k);
            }
        }
}

vector<int> getLeastNumbers(vector<int>& arr, int k) {
        QuickSort(arr,0,arr.size()-1, k);
        vector<int> v;
        for(int i=0;i<k;i++){
            v.emplace_back(arr[i]);
            cout<<v[i]<<" ";
        }
        cout<<endl;
        return v;
    }

int main(){
    vector<int> arr = {1,5,6,4,3,2,7,9,0,8};
    getLeastNumbers(arr,5);
}

1473.给房子涂色 III

在这里插入图片描述

给m个房子涂n种颜色
相邻房子不同色,cost[i][j]表示给i号房子涂j颜色代价,求最小上色总成本。力扣应该有原题

256.房子涂色

*解题思路:

房子i的最小涂色开销是房子i-1的最小涂色开销,加上房子i本身的涂色开销。但是房子i的涂色方式需要根据房子i-1的涂色方式来确定,所以我们对房子i-1要记录涂三种颜色分别不同的开销,这样房子i在涂色的时候,我们就知道三种颜色各自的最小开销是多少了。我们在原数组上修改,可以做到不用空间。

State: dp[i][j] // three colors: j = 0 or 1 or 2,

Function:

    dp[i][0] = dp[i][0] + min(dp[i - 1][1], dp[i -1][2])

    dp[i][1] = dp[i][1] + min(dp[i - 1][0], dp[i - 1][2])

    dp[i][2] = dp[i][2] + min(dp[i - 1][0], dp[i - 1][1])    

Initialize: dp = costs

Return: min(dp[n][0], dp[n][1], dp[n][2])
#include<bits/stdc++.h>
using namespace std;

void fun(vector<vector<int>>& costs){
	int size = costs.size();
	vector<vector<int>>dp = costs;
	for(int i=1;i<size;i++){
		dp[i][0] = dp[i][0] + min(dp[i-1][1], dp[i-1][2]);
		dp[i][1] = dp[i][1] + min(dp[i-1][0], dp[i-1][2]);
		dp[i][2] = dp[i][2] + min(dp[i-1][0], dp[i-1][1]);
	}
	cout<<min(dp[size-1][0], min(dp[size-1][1], dp[size-1][2]));
}

int main(){
	vector<vector<int>> costs = {{14,2,11},{11,14,5},{14,3,10}};
	fun(costs);
}

更新至2021.1.15

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值