题目300:最长递增子序列(NO)
- 解题思路:动态规划,就是dp[i]的运用,这里dp[i]表示第i个元素为结尾的最长子序列。
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的
子序列。
class Solution
{
public:
int lengthOfLIS(vector<int>& nums)
{
int n = nums.size();
if (n == 0)
{
return 0;
}
vector<int> dp(n, 0);
int max_count=0;
for (int i = 0; i < n; ++i)
{
dp[i] = 1;//如果前面没有比他小的了,那自己本身就是一个最长子序列
for (int j = 0; j < i; ++j)
{
if (nums[j] < nums[i])
{
//这里是每次扫描前面就记录最大的dp[i]
dp[i] = max(dp[i], dp[j] + 1);
}
}
if(dp[i]>max_count)
{
max_count=dp[i];
}
}
return max_count;
}
};
题目215:数组中的第k个最大元素(NO)
- 解题思路:使用快速排序算法
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
class Solution {
public:
void quickSort(std::vector<int>& arr, int left, int right)
{
if (left >= right)
{
return;
}
int pivot = arr[(left + right) / 2];
int i = left, j = right;
//这次一扫描,就会将整个数组划分为两个区域,以pivot为分界线
while (i <= j)
{
while (arr[i] < pivot)
{
//找到比中间结点大的
i++;
}
while (arr[j] > pivot)
{
//找到比中间结点小的
j--;
}
if (i <= j)
{
//交换两个元素
swap(arr[i], arr[j]);
i++;
j--;
}
}
//检查左边
if (left < j)
{
quickSort(arr, left, j);
}
//检查右边
if (i < right)
{
quickSort(arr, i, right);
}
}
int findKthLargest(vector<int>& nums, int k) {
int len=nums.size();
quickSort(nums,0,len-1);
return nums[len-k];
}
};
题目1:两数之和(YES)
- 解题思路:使用哈希表
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//使用哈希表
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++)
{
if(map.find(target-nums[i])!=map.end())
{
return {i,map[target-nums[i]]};
}
map[nums[i]]=i;
}
return {};
}
};
题目3:无重复字符的最长子串(NO)
- 解题思路:使用滑动窗口,就是和unordered_set搭配使用,用set来检查是否重复,没有重复在继续添加,如果重复了就删除掉第一个,因为是for一次遍历,所以每个i都可以成为第一个子串。
给定一个字符串 s ,请你找出其中不含有重复字符的 最长
子串的长度。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
//使用滑动窗口的方法,就是配合unordered_set来实现
//用set来判断是否出现了重复的字符,如果重复了就删除第一个,否则没重复的话
//就继续在set中添加字符
unordered_set<char>set;
int ans=0;
int temp=-1;
int len=s.size();
for(int i=0;i<s.size();i++)
{
//第一个字符是不用删除的
if(i!=0)
{
//删除第一个元素
set.erase(s[i-1]);
}
//以i为起点一直添加元素
//确保下一个位置合法才能够加入
while(temp+1<len&&!set.count(s[temp+1]))
{
set.insert(s[temp+1]);
temp++;
}
//说明遇到了重复的字符,保存这次的子串长度
ans=max(ans,temp-i+1);
}
return ans;
}
};
题目11:盛最多水的容器(YES)
-
解题思路:最刚开始第一想到的是使用双重for遍历一遍,让每个i都作为第一条边,每个的最大的面积,但是显然这样时间复杂度太高了。
-
错误示例
class Solution {
public:
int maxArea(vector<int>& height) {
//我现在突然想到的是使用双重for遍历,每个i都可以当容器的第一个位置
//然后找到其中最大的
int max_ans=0;
for(int i=0;i<height.size();i++)
{
for(int j=i+1;j<height.size();j++)
{
int area=(j-i)*min(height[i],height[j]);
max_ans=max(area,max_ans);
}
}
return max_ans;
}
};
-
后面我又想到了一个思路,既然是要计算面积,底乘以高,当然是保证底部越长越好,所以在大的情况从两边开始扫描一定不会漏掉。且在靠齐的时候,当然是向边长高的靠齐最好。
-
正确写法
class Solution {
public:
int maxArea(vector<int>& height) {
//此时我又想到了面积的算法是底乘以高,所以底应该尽可能的长,所以直接从两边开始扫描
//就行了
int left=0;
int right=height.size()-1;
int max_area=0;
while(left<right)
{
//从两边开始记录最大面积
int temp=(right-left)*min(height[right],height[left]);
if(temp>max_area)
{
max_area=temp;
}
if(height[left]<height[right])
{
//想要面积最大,应该向高的一边靠齐
left++;
}else
{
right--;
}
}
return max_area;
}
};
题目70:爬楼梯(YES)
- 典型的动态规划问题,考察队dp[i]数组的使用,这里dp[i]表示的是到达第i阶楼梯的方法数。这里需要关注的点是第3阶的方法数是第1阶和第2阶方法数的和。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
class Solution {
public:
int climbStairs(int n) {
//这题使用的是动态规划问题
//dp[i]的含义是到第i阶有多少不同的方法
vector<int>dp(n+1);
dp[0]=1;//单纯为了做题而设置的,没意义
dp[1]=1;
if(n<2)
{
return dp[n];
}
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
题目146:LRU缓存(NO)
- 解题思路:使用了双链表和哈希表的结合,这题值得关注。
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
//双向链表
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
unordered_map<int, DLinkedNode*> cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (!cache.count(key)) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
DLinkedNode* node = cache[key];
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if (!cache.count(key)) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode* node = new DLinkedNode(key, value);
// 添加进哈希表
cache[key] = node;
// 添加至双向链表的头部
addToHead(node);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode* removed = removeTail();
// 删除哈希表中对应的项
cache.erase(removed->key);
// 防止内存泄漏
delete removed;
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
DLinkedNode* node = cache[key];
node->value = value;
moveToHead(node);
}
}
void addToHead(DLinkedNode* node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
void removeNode(DLinkedNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
void moveToHead(DLinkedNode* node) {
removeNode(node);
addToHead(node);
}
DLinkedNode* removeTail() {
DLinkedNode* node = tail->prev;
removeNode(node);
return node;
}
};
题目200:岛屿数量(YES)
- 解题思路:用双重for遍历所有的元素,第一个进来的1就是一个岛屿,然后用广度优先遍历遍历这个岛屿的1,并使用check数组设置标记为访问过了。这里可以优化的地方就是使用dx[ ]和dy[ ]来控制方向
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
if(grid.empty()) {
return 0;
}
int row = grid.size();
int col = grid[0].size();
//用来标记是否访问过
vector<vector<bool>> check(row, vector<bool>(col, false));
int ans = 0;//记录岛屿的数量
//用来控制方向的 上 下 右 左
vector<int> dx = {1, -1, 0, 0};
vector<int> dy = {0, 0, 1, -1};
//将所有的元素都检查一遍
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
if(grid[i][j] == '1' && check[i][j] == false)
{
ans++;//能进来的就是一个岛屿
queue<pair<int, int>> que;
que.push({i, j});
check[i][j] = true;
while(!que.empty())
{
pair<int,int> temp = que.front();
que.pop();
for(int k = 0; k < 4; k++)
{
int x = temp.first + dx[k];
int y = temp.second + dy[k];
if(x >= 0 && x < row && y >= 0 && y < col && grid[x][y] == '1' && check[x][y] == false)
{
que.push({x, y});
check[x][y] = true;
}
}
}
}
}
}
return ans;
}
};
题目581:最短无序连续子数组(YES)
- 这题直接先用快速排序算法加上从left到right进行比较就行。
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
class Solution {
public:
//使用快速排序
void quick_sort(vector<int>&nums,int left,int right)
{
//递归跳出条件
if (left >= right)
{
return;
}
//定义中心轴
int mid=nums[(left+right)/2];
int j=right;
int i=left;
while(i<j)
{
while(nums[i]<mid) i++;
while(nums[j]>mid) j--;
//判断合法性
if(i<=j)
{
swap(nums[i],nums[j]);
i++;
j--;
}
}
//在判断一次左右
if(left<j)
{
quick_sort(nums,left,j);
}
if(i<right)
{
quick_sort(nums,i,right);
}
}
int findUnsortedSubarray(vector<int>& nums) {
vector<int>ans=nums;
quick_sort(nums,0,nums.size()-1);
int left=0;
int right=nums.size()-1;
int len=nums.size();
while(left<len&&nums[left]==ans[left])
{
left++;
}
while(right>=0&&nums[right]==ans[right])
{
right--;
}
if(left>right)
{
return 0;
}
return right-left+1;
}
};
题目94:二叉树的中序遍历(YES)
- 解题思路:二叉树中序遍历的算法,这个最容易记了。
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void inorder(TreeNode*root,vector<int>&ans)
{
if(root==nullptr)
{
return ;
}
inorder(root->left,ans);
ans.push_back(root->val);
inorder(root->right,ans);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int>ans;
inorder(root,ans);
return ans;
}
};
题目155:最小栈(NO)
- 解题思路:难点在于想不到用另外一个栈来表示最小的元素
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
class MinStack {
stack<int> x_stack;
stack<int> min_stack;
public:
MinStack() {
min_stack.push(INT_MAX);
}
void push(int x) {
x_stack.push(x);
min_stack.push(min(min_stack.top(), x));
}
void pop() {
x_stack.pop();
min_stack.pop();
}
int top() {
return x_stack.top();
}
int getMin() {
return min_stack.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
题目283:移动零(YES)
- 解题思路:使用双指针,left用来存储非零元素,right用来扫描非零元素,一旦right找到了,就里面放到left中。
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//用一个left指针来表示非零元素
int left=0;
int right=0;
while(right<nums.size())
{
if(nums[right]==0)
{
right++;
}else
{
//交换位置
swap(nums[left],nums[right]);
right++;
left++;
}
}
}
};
题目101:对称二叉树(YES)
- 解题思路:使用层序遍历,使用两个队列left_que和right_que两个遍历的顺序不同。分别进行判断。
给你一个二叉树的根节点 root , 检查它是否轴对称。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root == nullptr) {
return true; // 空树是对称的
}
queue<TreeNode*> left_que;
queue<TreeNode*> right_que;
left_que.push(root->left);
right_que.push(root->right);
while(!left_que.empty() && !right_que.empty()) {
TreeNode* temp_left = left_que.front();
TreeNode* temp_right = right_que.front();
left_que.pop();
right_que.pop();
//判断不合法条件
if((temp_left == nullptr && temp_right != nullptr) ||
(temp_left != nullptr && temp_right == nullptr)) {
return false;
} else if(temp_left == nullptr && temp_right == nullptr) {
continue;
}
if(temp_left->val != temp_right->val) {
return false;
}
//即便是空值也要入栈
left_que.push(temp_left->left);
left_que.push(temp_left->right);
right_que.push(temp_right->right);
right_que.push(temp_right->left);
}
if(!left_que.empty() || !right_que.empty()) {
return false;
}
return true;
}
};
题目543:二叉树的直径(NO)
- 解题思路:深度优先遍历
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
int ans;
int depth(TreeNode* rt){
if (rt == NULL) {
return 0; // 访问到空节点了,返回0
}
int L = depth(rt->left); // 左儿子为根的子树的深度
int R = depth(rt->right); // 右儿子为根的子树的深度
ans = max(ans, L + R + 1); // 计算d_node即L+R+1 并更新ans
return max(L, R) + 1; // 返回该节点为根的子树的深度
}
public:
int diameterOfBinaryTree(TreeNode* root) {
ans = 1;
depth(root);
return ans - 1;
}
};
- 解析
这段代码实际上是在通过递归的方式来计算二叉树的直径,直径是指二叉树中任意两个节点之间的最长路径的节点数。
在这段代码中,主要使用了两个函数:
depth 函数:这是一个递归函数,用于计算以当前节点为根的子树的深度。在该函数中,首先判断当前节点是否为空,若为空则返回深度 0。然后递归计算左子树和右子树的深度(分别记为L和R),并返回左右子树深度的较大值加上 1(加 1 是因为当前节点也要算在内)。在递归的过程中,还会更新一个全局变量 ans,该变量记录了经过当前节点的路径的节点数之和的最大值。
diameterOfBinaryTree 函数:这是一个公开的函数,用户通过调用这个函数来计算二叉树的直径。在这个函数中,首先初始化变量 ans 为 1,然后调用 depth 函数来计算整棵二叉树的直径。最后返回 ans - 1,因为题目要求的是节点数而非边数。
综上所述,这段代码通过递归的方式遍历二叉树,计算每个节点为根节点的子树的深度,并在递归过程中更新最长路径的节点数。最终得出二叉树的直径。
题目461:汉明距离(NO)
- 解题思路:通过以为操作来进行判断
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
class Solution {
public:
int hammingDistance(int x, int y) {
int s = x ^ y, ret = 0;
while (s) {
ret += s & 1;
s >>= 1;
}
return ret;
}
};
这段代码实现了计算两个整数 x 和 y 之间的汉明距离的功能。汉明距离是指两个整数的二进制表示中对应位不同的位数。
在代码中,首先计算两个整数 x 和 y 的按位异或结果,将结果存储在变量 s 中。然后使用一个循环,不断右移变量 s,并在每次循环中获取最右边的位(即获取最低位),如果该位为1则表示在该位置上 x 和 y 的二进制表示不同,累加到返回值 ret 中。最后返回 ret,即 x 和 y 之间的汉明距离。
通过这种方法,代码有效地计算了两个整数之间的汉明距离。
- 知识点补充
在C++中,右移操作符 >> 对于有符号整数和无符号整数的处理方式是不同的:
对于有符号整数:
如果整数是有符号的(int、short、char等),右移操作会保持符号位不变。即,对于正数,右移时在最高位补0;对于负数,右移时在最高位补1。这种方式称为“带符号右移”。
对于无符号整数:
如果整数是无符号的(unsigned int、unsigned short、unsigned char等),右移操作会在最高位始终补0。这种方式称为“无符号右移”。
在给定的代码中,变量 s 的类型没有特别指定,因此默认情况下会被视为有符号整数。因此,右移操作 s >>= 1 会保持符号位不变,即在右移的过程中,负数的最高位会一直被补1,而不会被移除。因此,右移操作并不会去掉最低位,而是将整个 s 数字向右移动一位,新的最高位会根据原来的符号位进行填充。
题目448:找到数组中消失的数字(YES)
- 解题思路:使用哈希表记录已有的数字,然后从1开始for遍历一遍。
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
//使用哈希表来存储
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++)
{
map[nums[i]]++;
}
vector<int>ans;
for(int i=1;i<=nums.size();i++)
{
if(map[i]==0)
{
ans.push_back(i);
}
}
return ans;
}
};
题目141:环形链表(YES)
- 这题显然使用哈希表是最简单的。
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
//使用哈希表显然是最简单的
unordered_map<ListNode*,int>map;
ListNode*temp=head;
while(temp!=NULL&&!map.count(temp))
{
map[temp]++;
temp=temp->next;
}
if(temp==NULL)
{
return false;
}
return true;
}
};
题目155:最小栈(NO)
- 这题要使用两个栈,一个当正常的栈来使用,另外一个用来存放最小的元素。关键的地方是刚开是最小栈要放置INT_MAX,后面每次和放进的元素进行比较,放入最小的。
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
class MinStack {
public:
//这道题使用两个栈
stack<int>s1;//这里用来正常使用栈的功能
stack<int>min_s2;//这里用用来放置最小的元素
int num_min;
MinStack() {
num_min=INT_MAX;
min_s2.push(num_min);
}
void push(int val) {
s1.push(val);
//将最小元素入栈
min_s2.push(min(min_s2.top(),val));
}
void pop() {
s1.pop();
min_s2.pop();
}
int top() {
return s1.top();
}
int getMin() {
return min_s2.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
题目226:翻转二叉树(YES)
- 解题思路:使用层序遍历每一个节点,并交换它的左右孩子节点。
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr)
{
return root;
}
//使用层序遍历每个节点,并交换左右节点
queue<TreeNode*>que;
que.push(root);
while(!que.empty())
{
int size=que.size();
for(int i=0;i<size;i++)
{
TreeNode*current=que.front();
que.pop();
TreeNode*current_left=current->left;
TreeNode*current_right=current->right;
//交换节点
current->left=current_right;
current->right=current_left;
if(current->left!=nullptr)
{
que.push(current->left);
}
if(current->right!=nullptr)
{
que.push(current->right);
}
}
}
return root;
}
};
题目234:回文链表(YES)
- 解题思路:使用数组来存储链表的数据,最后判断数组是否对称就行。
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
//先使用最简单的方法来解决,使用数组的存储一遍链表的数据
vector<int>ans;
ListNode*temp=head;
while(temp!=nullptr)
{
ans.push_back(temp->val);
temp=temp->next;
}
//检查ans是否对称
int j=ans.size()-1;
int i=0;
while(i<j)
{
if(ans[i]==ans[j])
{
i++;
j--;
}else
{
return false;
}
}
return true;
}
};
题目617:合并二叉树(NO)
- 解题思路:这题递归的方法并不好想,暂时先记住解题思路吧。
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == nullptr) {
return t2;
}
if (t2 == nullptr) {
return t1;
}
auto merged = new TreeNode(t1->val + t2->val);
merged->left = mergeTrees(t1->left, t2->left);
merged->right = mergeTrees(t1->right, t2->right);
return merged;
}
};
题目415:字符串相加(NO)
- 解题思路:就使用模拟的方法,从最低位开始相加,主要需要注意的是
char转成int是'1'-'0'=1
- int 转成 char是
1+'0'='1'
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
class Solution {
public:
string addStrings(string num1, string num2) {
int num1_size = num1.size() - 1;
int num2_size = num2.size() - 1;
// 确保 ans 初始化为空字符串
string ans;
int count = 0; // 进位位
int i = 0; // 结果字符串的索引
while (num1_size >= 0 || num2_size >= 0 || count > 0)
{
int num1_num = (num1_size >= 0) ? (num1[num1_size] - '0') : 0;
int num2_num = (num2_size >= 0) ? (num2[num2_size] - '0') : 0;
// 计算当前位的和
int sum = num1_num + num2_num + count;
count = sum / 10; // 更新进位
ans.insert(ans.begin(), (sum % 10) + '0'); // 把当前位的结果插入ans 的开头
num1_size--;
num2_size--;
}
return ans;
}
};
题目14:最长公共前缀(YES)
- 解题思路:直接暴力解,比较每一个成员前缀就行。
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans="";
//直接暴力解就行
int i=0;
while(i<strs[0].size())
{
char num=strs[0][i];
for(int j=0;j<strs.size();j++)
{
if(i>=strs[j].size()||strs[j][i]!=num)
{
return ans;
}
}
ans+=num;
i++;
}
return ans;
}
};
题目88:合并两个有序数组(YES)
- 解题思路:使用快速排序,切记快速排序只有找要交换的 i 和 j 才严格不等好,其他都要等号。
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
class Solution {
public:
void quick_sort(vector<int>&nums1,int left,int right)
{
//递归结束条件
if(left>=right)
{
return ;
}
int mid=nums1[(right+left)/2];
int i=left;
int j=right;
while(i<=j)
{
while(nums1[i]<mid)
{
//这里要严格小于
i++;
}
while(nums1[j]>mid)
{
j--;
}
if(i<=j)
{
swap(nums1[i],nums1[j]);
i++;
j--;
}
}
//检查一下左右
if(i<=right)
{
quick_sort(nums1,i,right);
}
if(left<=j)
{
quick_sort(nums1,left,j);
}
}
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
//先将num2合并到num1中,最后使用快排进行一次排序就行
for(int i=0;i<n;i++)
{
nums1[m]=nums2[i];
m++;
}
int left=0;
int right=nums1.size()-1;
quick_sort(nums1,left,right);
}
};
题目125:验证回文串(YES)
- 解题思路:使用库函数std::tolower()。
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
class Solution {
public:
bool isPalind(string ans)
{
int size=ans.size();
int left=0;
int right=size-1;
while(left<=right)
{
if(ans[left]!=ans[right])
{
return false;
}
left++;
right--;
}
return true;
}
bool isPalindrome(string s) {
string ans;
//遍历s将字母剥离出来
for(int i=0;i<s.size();i++)
{
if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z'))
{
ans+=std::tolower(s[i]);
}else if(s[i]>='0'&&s[i]<='9')
{
ans+=std::tolower(s[i]);
}
}
std::cout<<ans<<std::endl;
return isPalind(ans);
}
};
题目67:二进制求和(NO)
- 解题思路: 模拟二进制求和
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。
class Solution {
public:
string addBinary(string a, string b) {
string ans;
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int n = max(a.size(), b.size()), carry = 0;
for (int i = 0; i < n; ++i)
{
// 将当前位的值加到 carry 中
if (i < a.size())
{
// 检查 a 是否还有位
carry += (a.at(i) == '1') ? 1 : 0;
}
if (i < b.size())
{
// 检查 b 是否还有位
carry += (b.at(i) == '1') ? 1 : 0;
}
// 当前位的结果
ans.push_back((carry % 2) ? '1' : '0');
// 更新进位
carry /= 2; // 进位为 carry / 2
}
// 如果最后还有进位,添加 '1'
if (carry)
{
ans.push_back('1');
}
// 反转结果并返回
reverse(ans.begin(), ans.end());
return ans;
}
};
题目144:二叉树的前序遍历(YES)
- 解题思路:前序递归算法。
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void preorder(TreeNode*root,vector<int>&ans)
{
if(root==nullptr)
{
return ;
}
ans.push_back(root->val);
preorder(root->left,ans);
preorder(root->right,ans);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int>ans;
preorder(root,ans);
return ans;
}
};
题目235:二叉搜索树的最近公共祖先(NO)
- 解题思路:先将每个节点二叉搜索树的搜索路径列出,对比这个搜索路径,找到最后一个相同的公共节点。
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
/**
* 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:
vector<TreeNode*>find_path(TreeNode*root,TreeNode*target)
{
//这里的搜索路径要包含自己
vector<TreeNode*>path;
TreeNode*temp=root;
while(temp!=target)
{
path.push_back(temp);
if(target->val<temp->val)
{
temp=temp->left;
}else
{
temp=temp->right;
}
}
path.push_back(target);
return path;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*>path_p=find_path(root,p);
vector<TreeNode*>path_q=find_path(root,q);
//找到路径中最后一个相同的
TreeNode*ans=NULL;
int count=min(path_p.size(),path_q.size());
for(int i=0;i<count;i++)
{
if(path_p[i]==path_q[i])
{
ans=path_p[i];
}
}
return ans;
}
};
面试题08.06:汉诺塔问题(NO)
- 解题思路:递归思想,不要想。
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
class Solution {
public:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C)
{
int n = A.size();
move(n, A, B, C);
}
void move(int n, vector<int>& A, vector<int>& B, vector<int>& C)
{
if (n == 1)
{
C.push_back(A.back());
A.pop_back();
return;
}
move(n-1, A, C, B); // 将A上面n-1个通过C移到B
C.push_back(A.back()); // 将A最后一个移到C
A.pop_back(); // 这时,A空了
move(n-1, B, A, C); // 将B上面n-1个通过空的A移到C
}
};
题目812:最大三角形面积(NO)
- 解题思路:枚举。
给你一个由 X-Y 平面上的点组成的数组 points ,其中 points[i] = [xi, yi] 。从其中取任意三个不同的点组成三角形,返回能组成的最大三角形的面积。与真实值误差在 10-5 内的答案将会视为正确答案。
class Solution {
public:
double triangleArea(int x1, int y1, int x2, int y2, int x3, int y3) {
return 0.5 * abs(x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2);
}
double largestTriangleArea(vector<vector<int>> & points) {
int n = points.size();
double ret = 0.0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
ret = max(ret, triangleArea(points[i][0], points[i][1], points[j][0], points[j][1], points[k][0], points[k][1]));
}
}
}
return ret;
}
};