尽人事 - 剑指offer

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;
    }
};

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]matrix[row - 1][mid]<target<matrix[row][mid]matrix[row−1][mid]<target<matrix[row][mid] 的点,比如当 row=3,mid=2row=3,mid=2row=3,mid=2 时(黄色区域),9<target<149<target<149<target<14,这时我们可以判断出来 targettargettarget 一定在左下或者右上区域:

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

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

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

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

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

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

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

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

扫描二维码关注公众号,回复: 11938355 查看本文章
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;

    }
};

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;

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);
    }
};

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.二叉树后序遍历序列

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,即使用的全局变量。

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;
    }
};

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;
    }
};

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;
    }
};

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;
        }
    }
};

猜你喜欢

转载自blog.csdn.net/s11show_163/article/details/106874243