03.数组中的重复数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1
的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
二重循环失败原因:超时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;
}
}
- 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。
先查找左边界,再锁定到具体行,行内排查。失败:并不是下一行开头就比上一行结尾大,题意理解不当。- 找规律:
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;
}
};
-
递归
在 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就可以了.
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层
旋转的时候,每四个矩阵块作为一组进行相应的旋转
可以看出,第二次旋转的时候比第一次旋转偏移了一格,这里我们使用add变量来记录矩阵块的偏移量,首先不考虑偏移量的时候写出左上角的坐标为(pos1,pos1),右上角的坐标为(pos1,pos2),左下角的坐标为(pos2,pos1),右下角的坐标为(pos2,pos2),则能够写出偏移之后对应的坐标
每次计算完一层之后,矩阵向内收缩一层,
所以有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;
}
}
};