目录
128. 最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
//使用Hash表记录所有的元素,起到去重功能
//朴素的思路:扫描每个元素x,往后枚举x+1,...,x+y,判断其是否存在于nums中,每次维持记录一个最大长度,这样的复杂度为O(n^3)
//进阶的思路:判断枚举的元素x+i在不在nums中,需要O(n)复杂度,在这里我们引入一个哈希表unordered_set,即可降低复杂度到O(1)
//最终的进阶:在扫描元素时,比如x:我们会依次枚举x+1,...,x+y是否在哈希表中,在这里我们只需要判断x前驱x-1在不在其中判断是否需要进行枚举
if (nums.size() == 0)
return 0;
unordered_set<int> num_set;
for(int i = 0; i < nums.size(); i++){
num_set.insert(nums[i]);
}
int len = 1;
for(const auto&num: num_set){
int cur = num;
//当cur-1不在哈希表中,需要对当前的cur进行扫描遍历
if(!num_set.count(cur-1)){
while(num_set.count(cur)){
cur += 1;
}
int now_len = cur - num;
len = len > now_len ? len: now_len;
}
}
return len;
}
//思路2:哈希表记录右边界。在上诉思路中,对cur枚举时,需要逐个+1枚举到达cur的右界,在这里,有一个进阶思路,维持一个Hash表,记录每个元素num能够到达的右边界。
int longestConsecutive_2(vector<int>& nums){
map<int, int> num_map;
for(const auto&num: nums){
//初始化map,每个元素num的右边界为自身
num_map.insert(pair<int, int>(num, num));
}
int len = 0;
for(const auto&num: nums){
int right = num_map(cur);
while(num_map.count(right+1)){
right = num_map[right+1];
}
//更新右边界
num_map[num] = right;
len = len > right - num + 1 ? len : right - num + 1;
}
return len;
}
//思路3:动态规划。与思路2类似,该思路也是为了减少逐个+1枚举的时间,与思路2的对比在于,该思路对于元素num同时向左边界和右边界进行扩充
int longestConsecutive_3(vector<int>& nums){
map<int, int> num_map;
int len = 0;
for(const auto&num: nums){
//当map中不包含num时,即num第一次出现
if(!num_map.count(num)){
//left为num-1所在连续区间的长度,进一步理解为左连续区间的长度,若不存在,则取0
int left = num_map[num-1];
//right为num+1所在连续区间的长度,进一步理解为右连续区间的长度,若不存在,则取0
int right = num_map[num+1];
int cur_len = left + right + 1;
len = len > cur_len ? len : cur_len;
//将num加入到map中,表示其已经被遍历过
num_map[num] = -1;
num_map[num - left] = cur_len;
num_map[num + right] = cur_len;
}
}
return len;
}
//思路4:并查集。与思路2类似,也是用来记录右边界,所有在同一个连续区间内的元素都会在一个连通分量中,且这些元素的根节点都为最远的右边界元素
//具体思路如下:
//1.遍历所有元素num,如果num+1存在,则将其加入到num+1所在的连通分量中
//2.遍历所有元素num,通过find函数找到num所在分量的根节点,也就是最右边界,从而求得连续区间的长度
int longestConsecutive_4(vector<int>& nums){
}
};
136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:输入: [4,1,2,1,2]
输出: 4
class Solution {
public:
int singleNumber(vector<int>& nums) {
//异或实现
int ans = 0;
for(const auto&num: nums){
ans ^= num;
}
return ans;
}
};
169. 多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:输入:[2,2,1,1,1,2,2]
输出:2
class Solution {
public:
int majorityElement(vector<int>& nums) {
/*
投票法
count:当前认为是众数的数出现的次数-其他数出现的次数
*/
int count = 0, val = 0;
for(const auto& num: nums){
if(count == 0){
val = num;
count += 1;
}else{
if(val == num)
count += 1;
else
count -= 1;
}
}
return val;
}
};
238. 除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
//使用数组记录前缀乘积和后缀乘积
vector<int> prefix(nums.size(), 1);
vector<int> suffix(nums.size(), 1);
vector<int> product;
for(int i = 1; i < nums.size(); i++){
prefix[i] = nums[i-1] * prefix[i-1];
}
for(int i = nums.size()-2; i >= 0; i--){
suffix[i] = suffix[i+1] * nums[i+1];
}
for(int i = 0; i < nums.size(); i++){
product.push_back(prefix[i]*suffix[i]);
}
return product;
}
};
74. 搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-a-2d-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
//二分搜索
int m = matrix.size(), n = matrix[0].size();
int low = 0, high = m * n - 1;
while(low <= high){
int mid = low + ((high-low) >> 1);
int i = mid / n, j = mid % n;
if(matrix[i][j] == target)
return true;
else if(matrix[i][j] > target)
high = mid - 1;
else
low = mid + 1;
}
return false;
}
};
240. 搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入: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
示例 2:
输入: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 = 20
输出:false
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
/*法1,Z字形查找,从以矩阵的右上角元素(0,n-1)为右上角的元素、矩阵左下角为原始矩阵的左下角进行搜索
假设当前搜索矩阵的右上角坐标为(x, y),则矩阵的所处的行范围为[x, m-1],所处的列范围为[0, y]。
若当前坐标(x, y)元素刚好为target,则直接返回即可。
若当前坐标(x, y)元素大于target,又因为目前被搜索矩阵的第y列的元素大于target,所以调整搜索矩阵的左上角为(x, y-1)
若当前坐标(x, y)元素小于target,又因为目前搜索矩阵第x行的元素小于target,所以应该调整搜索矩阵的左上角为(x+1, y)
*/
int x = 0, y = matrix[0].size() - 1;//分别代表行数和列数
while(x < matrix.size() && y >= 0){
if(matrix[x][y] == target)
return true;
else if(matrix[x][y] > target)
y--;
else
x++;
}
return false;
}
};
338. 比特位计数
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
class Solution {
public:
int count(int n){
int num = 0;
while(n){
n &= (n-1);
num += 1;
}
return num;
}
vector<int> countBits(int n) {
/*位运算的一个规律:
N & N-1每次能将N的二进制的最低位的1去掉(不论N的奇、偶)
*/
vector<int> ans;
for(int i = 0; i <= n; i++)
ans.push_back(count(i));
return ans;
}
};
406. 根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
示例 1:
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
示例 2:输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
模拟的思想:
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//从低到高考虑
vector<vector<int>> ans(people.size(), vector<int>(2, -1));
sort(people.begin(), people.end());//按照第0列进行排序
int last = -1, now = -1, index;
for(int i = 0; i < people.size(); i++){
now = people[i][0];//元素值
index = people[i][1];//索引处
int num = 0, tar_index = 0;//目标元素在结果vector中的索引
while(num < index + 1){
if(ans[tar_index][0] == -1 || ans[tar_index][0] == now){
num++;
}
tar_index += 1;
}
ans[tar_index-1][0] = now;
ans[tar_index-1][1] = people[i][1];
}
return ans;
}
vector<vector<int>> reconstructQueue_nixu(vector<vector<int>>& people) {
//刚刚前一种思路是用模拟升序的思路来的,但是我们可以注意到题目中k的给出就是身高大于等于h_i的人数
//所以我们应该对第一个元素进行降序排列,这样当前vector的大小就是大于等于当前元素的个数
//并对第二个元素升序排序,为何要对第二个升序,比如<5,2>和<5,5>,若先插入<5,5>,再插入<5,2>
//<5,2>对应的5肯定在<5,5>前面,则导致<5,5>变成了<5,6>
sort(people.begin(), people.end(), [](const vector<int> &a, const vector<int> &b){
if(a[0] != b[0]){
return a[0] > b[0];
}
return a[1] < b[1];
}
);
vector<vector<int>> ans;
for(const auto& i: people){
if(ans.size() == i[1]){
ans.push_back(i);
}else if(ans.size() > i[1]){
ans.insert(ans.begin()+i[1], i);
}
}
return ans;
}
};
647. 回文子串
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
int countSubstrings(string s) {
//动态规划:dp[i][j]表示由i--j能否构成回文子串
//dp[i][j] = dp[i+1][j-1] (s[i]==s[j])
int n = s.size();
vector<vector<bool>> dp(n, vector<bool>(n, false));
//这里要注意扫描的顺序,i应该从右往左扫描,这样才能确保之前的依赖值已经得到
int count = 0;
for(int i = n-1; i >= 0; i--)
for(int j = i; j < n; j++){
if(i == j){
//扫描到同一个字符
dp[i][j] = true;
}else if(j - i >= 2){
//3个以及3个以上的字符
dp[i][j] = dp[i+1][j-1] && (s[i] == s[j]);
}else{
//两个字符
dp[i][j] = (s[i] == s[j]);
}
count += dp[i][j];
}
return count;
//另解:对于每个回文串,它必定是以某个字符为中心朝两边展开的,
//有疑问在于回文串长度可能为奇数也可能为偶数
//参考官方题解提出的一种方法灵活化解了这一问题
//只是在编程实现上更加方便,思想其实还是在按奇数或者偶数展开尝试
int l, r, n, count;
n = s.length();
count = 0;
for(int i = 0; i < 2 * n - 1; i++){
l = i / 2;
r = i / 2 + i % 2;
while(l >= 0 && r < n && s[l] == s[r]){
count++;
l -= 1;
r += 1;
}
}
return count;
}
};
146. LRU 缓存
请你设计并实现一个满足 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) 的平均时间复杂度运行。示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
#include "bits/stdc++.h"
using namespace std;
struct Node{
int key;
int value;
Node* next;
Node* pre;
Node(){
next = nullptr;
pre = nullptr;
}
Node(int key, int value):key(key), value(value){
next = nullptr;
pre = nullptr;
}
};
class LRUCache {
public:
unordered_map<int, Node*> val_index;
int cap;
int num;
Node* head;
Node* tail;
LRUCache(int capacity) {
/*哈希表+双向链表(头节点+尾节点)
哈希表存储key对应的位置,双链表头部:最近被使用过的元素;双链表的尾部:最近未被使用的元素
*/
cap = capacity;
num = 0;
head = new Node();
tail = new Node();
head->next = tail;
tail->pre = head;
}
int get(int key) {
if(val_index.find(key) == val_index.end())
//不能找到
return -1;
else{
//能找到
Node* now = val_index[key];
move_to_head(now);
return now->value;
}
}
void insert_head(Node *now){
//将节点now插入到头节点
now->next = head->next;
now->pre = head;
head->next = now;
now->next->pre = now;
}
Node* delete_tail(){
//删除尾部节点并返回尾部节点
Node* l = tail->pre;
Node *pre = tail->pre->pre;
pre->next = tail;
tail->pre = pre;
return l;
}
void move_to_head(Node* now){
//将当前节点移动到头节点
Node* pre = now->pre;//取出当前元素在链表中的前一个
Node* next = now->next;//取出当前元素在链表中的后一个
//pre和next之间衔接
pre->next = next;
next->pre = pre;
insert_head(now);
}
void put(int key, int value) {
if(val_index.find(key) != val_index.end()){
//元素已经出现过,取出放到队头
Node* now = val_index[key];
now->value = value;
move_to_head(now);
}
else if(num != cap){
//未满 元素没有出现过,直接插入到队头
num++;
Node* now = new Node(key, value);
val_index[key] = now;
insert_head(now);
}else{
//未出现过且满了
Node* l = delete_tail();//删除尾节点并返回
Node* now = new Node(key, value);
insert_head(now);
val_index.erase(l->key);//在map中删除其对应的位置
val_index[key] = now;
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
31. 下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:输入:nums = [1,1,5]
输出:[1,5,1]
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
void swap_one(vector<int>& nums, int i, int j){
//找出i+1---j区间中刚好大于nums[i]的数
int index = j;
while(index > i && nums[index] <= nums[i]){
//找出第一个大于nums[i]的数
index--;
}
// cout << "one index " << index << endl;
swap(nums[i], nums[index]);
}
void swap_nums(vector<int>& nums, int i, int j){
//对区间i---j的元素进行交换
if(i == j)
return;
for(int a = i, b = j; a <= b; a++, b--)
swap(nums[a], nums[b]);
}
void nextPermutation(vector<int>& nums) {
//下一个字典排序
//从最后一个元素逆序扫描到第一个元素,希望是递减的,那我就可以形成下一个字典序了
bool flag = false;//用来标记当前逆序扫描递增与否
// int index;
for(int i = nums.size() - 1; i - 1 >= 0; i--){
//从最右边开始扫描找到递减处
if(nums[i] > nums[i-1]){
// cout << "now index " << i << endl;
//交换nums[i-1]与i---nums.size()-1稍大的数交换
swap_one(nums, i-1, nums.size()-1);
//再对i---nums.size()-1进行对称交换
swap_nums(nums, i, nums.size()-1);
// reverse(nums.begin()+i, nums.end());
flag = true;
break;
}
}
if(!flag){
//该数组逆序是递减的,没法构成下一个升序的字典序
swap_nums(nums, 0, nums.size()-1);
// reverse(nums.begin(), nums.end());
}
}
};
class Solution_88 {
public:
void nextPermutation(vector<int>& nums) {
//从右往左找到第一个下降处说明可以构成一个新的下一个字典序的数
//然后做交换操作,交换完成之后只得到了字典序的头,并不是下一个字典序,所以接下来的数字要是严格递增的
int i = nums.size() - 2;//下降处
while(i >= 0 && nums[i+1] <= nums[i]){
i--;
}
if(i < 0)//找不到下降处
sort(nums.begin(), nums.end());
else{
//寻找有侧中大于下降的值作交换
int j = nums.size() - 1;
while(j > i && nums[j] <= nums[i]){
j--;
}
swap(nums[i], nums[j]);
//例如:5 9 8 7 3 2
//找到了5和7交换但现在7 9 8 5 3 2明显不是结果,相当于我们只确定了头,还得继续对剩余数据做升序
int p = i + 1, q = nums.size() - 1;
while(p <= q){
swap(nums[p++], nums[q--]);
}
}
}
};
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int z = 0; z < n / 2; z++){
//由外到内进行旋转一圈一圈地按边进行旋转(复杂了,其实可以直接一个一个元素地进行旋转)
int left_up_i = z, left_up_j = z, right_up_i = z, right_up_j = n - 1 - z;
int left_down_i = n - 1 - z, left_down_j = z, right_down_i = n - 1 - z, right_down_j = n - 1 - z;
//临时存储最上边的值
vector<int> temp;
for(int j = left_up_j; j <= right_up_j; j++){
temp.push_back(matrix[left_up_i][j]);
}
//最左边到最上边旋转
for(int i = left_up_i; i <= left_down_i; i++){
matrix[left_up_j][n - 1 - i] = matrix[i][left_up_j];
}
//最下边到最左边旋转
for(int j = left_down_j; j <= right_down_j; j++){
matrix[j][n - 1 - left_down_i] = matrix[left_down_i][j];
}
//最右边到最下边的旋转
for(int i = right_down_i; i >= right_up_i; i--){
matrix[right_up_j][n - 1 - i] = matrix[i][right_up_j];
}
//最上边到最右边的旋转
int k = 0;
for(int i = right_up_i; i <= right_down_i; i++){
matrix[i][right_down_j] = temp[k++];
}
}
}
void rotate_node(vector<vector<int>>& matrix) {
//另解:一圈一圈由外到内进行旋转,每次旋转四个元素
//分块处理:对于n为奇数和偶数处理方式不一样,可以参考官方题解中对于i和j的巧妙处理
//https://leetcode-cn.com/problems/rotate-image/solution/xuan-zhuan-tu-xiang-by-leetcode-solution-vu3m/
int n = matrix.size();
for(int i = 0; i < n / 2; i++)
for(int j = 0; j < (n + 1) / 2; j++){
int temp = matrix[i][j];
//左边的交换到上面来
matrix[i][j] = matrix[n-1-j][i];
//下面的交换到左边来
matrix[n-1-j][i] = matrix[n-1-i][n-1-j];
//右边的交换到下面来
matrix[n-1-i][n-1-j] = matrix[j][n-1-i];
//上边的交换到右边
matrix[j][n-1-i] = temp;
}
}
void rotate_fanzhuan(vector<vector<int>>& matrix) {
//翻转法
//水平翻转+沿对角线翻转
int n = matrix.size();
for(int i = 0; i < n/2; i++)
for(int j = 0; j < n; j++){
swap(matrix[i][j], matrix[n-1-i][j]);
}
//沿对角线翻转
for(int i = 0; i < n; i++)
for(int j = 0; j < i; j++){
swap(matrix[i][j], matrix[j][i]);
}
}
};
59. 螺旋矩阵 II
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:输入:n = 1
输出:[[1]]来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
//使用状态机来表示
vector<vector<int>> ans(n, vector<int>(n, 0));
int i = 0, j = 0, num = 1;
//1:往右走,2:往下走,3:往左走,4:往上走
int state = 1;//表示当前坐标继续的状态
while(num != n * n + 1){
ans[i][j] = num++;
if(state == 1){
if(j+1 >= n || ans[i][j+1] != 0){
//到右边界,或者右边格点值不为0
i++;
state = 2;
}else{
j++;
}
}else if(state == 2){
if(i+1 >= n || ans[i+1][j] != 0){
//到下边界,或者下边格点值不为0
j--;
state = 3;
}else{
i++;
}
}else if(state == 3){
if(j-1 < 0 || ans[i][j-1] != 0){
//到左边界,或者左边格点值不为0
i--;
state = 4;
}else{
j--;
}
}else if(state == 4){
if(i-1 < 0 || ans[i-1][j] != 0){
//到上边界,或者上面格点值不为0
j++;
state = 1;
}else{
i--;
}
}
}
return ans;
}
vector<vector<int>> generateMatrix_other_simply(int n) {
//使用状态机来表示
vector<vector<int>> ans(n, vector<int>(n, 0));
int i = 0, j = 0, num = 1;
//1:往右走,2:往下走,3:往左走,4:往上走
int state = 0;
vector<vector<int>> dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//右、下、左、上
while(num != n * n + 1){
ans[i][j] = num++;
int next_i, next_j;
next_i = i + dir[state][0];
next_j = j + dir[state][1];
if(next_i < 0 || next_i >= n || next_j < 0 || next_j >= n || ans[next_i][next_j] != 0){
state = (state + 1) % 4;
}
i = i + dir[state][0];
j = j + dir[state][1];
}
return ans;
}
vector<vector<int>> generateMatrix_ancengbianli(int n) {
//按层遍历
vector<vector<int>> ans(n, vector<int>(n, 0));
int left = 0, right = n-1, top = 0, down = n-1, num = 1;
//1:往右走,2:往下走,3:往左走,4:往上走
while(num != n * n + 1){
for(int j = left; j <= right; j++)
ans[top][j] = num++;
top++;//top层下移
for(int i = top; i <= down; i++)
ans[i][right] = num++;
right--;//right层左移
for(int j = right; j >= left; j--)
ans[down][j] = num++;
down--;//down层上移
for(int i = down; i >= top; i--)
ans[i][left] = num++;
left++;//left层右移
}
return ans;
}
};
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
int up = 0, down = m - 1, left = 0, right = n - 1;
vector<int> ans;
while(up <= down && left <= right){
//先往右
for(int i = left; i <= right; i++)
ans.push_back(matrix[up][i]);
up++;
if(up > down) break;
//再往下
for(int i = up; i <= down; i++)
ans.push_back(matrix[i][right]);
right--;
if(left > right) break;
//再往左
for(int i = right; i >= left; i--)
ans.push_back(matrix[down][i]);
down--;
if(up > down) break;
//再往上
for(int i = down; i >= up; i--)
ans.push_back(matrix[i][left]);
left++;
if(left > right) break;
}
return ans;
}
};
208. 实现 Trie (前缀树)
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
#include "bits/stdc++.h"
using namespace std;
struct node{
char now;
vector<node*> next;
bool isend;
node(char now_):now(now_){
next.resize(26, nullptr);
isend = false;
}
};
class Trie {
public:
node* start;
Trie() {
start = new node('*');//字符*作为开始字符
start->isend = true;
}
void insert(string word) {
node* now = start;
for(int i = 0; i < word.size(); i++){
char c = word[i];
if(now->next[c-'a'] == nullptr){
//为空,新建节点并指向那一个节点
node *p = new node(c);
now->next[c-'a'] = p;
}
now = now->next[c-'a'];
}
now->isend = true;
}
bool search(string word) {
node* now = start;
for(int i = 0; i < word.size(); i++){
char c = word[i];
if(now->next[c-'a'] == nullptr)
return false;
else
now = now->next[c-'a'];
}
return now->isend;
}
bool startsWith(string prefix) {
node* now = start;
for(int i = 0; i < prefix.size(); i++){
char c = prefix[i];
if(now->next[c-'a'] == nullptr)
return false;
else
now = now->next[c-'a'];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
提示:
1 <= x <= 9
最多调用 100 次 push、pop、peek 和 empty
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)
进阶:
你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。
class MyQueue {
public:
stack<int> st_one;
stack<int> st_two;
MyQueue() {
}
void push(int x) {
st_one.push(x);
}
int pop() {
while(st_one.size() != 1){
int now = st_one.top();
st_one.pop();
st_two.push(now);
}
int top = st_one.top();
st_one.pop();
while(!st_two.empty()){
int now = st_two.top();
st_two.pop();
st_one.push(now);
}
return top;
}
int peek() {
while(st_one.size() != 1){
int now = st_one.top();
st_one.pop();
st_two.push(now);
}
int top = st_one.top();
st_one.pop();
st_two.push(top);
while(!st_two.empty()){
int now = st_two.top();
st_two.pop();
st_one.push(now);
}
return top;
}
bool empty() {
return st_one.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
69. x 的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:输入:x = 8
输出:2
class Solution {
public:
int mySqrt(int x) {
//直接枚举i=1----sqrt(x):复杂度:O(sqrt(n))
//另解:二分查找:log(n)
int pre = 0;
for(int i = 1; i <= x; i++){
if((long long)i * i > x){
break;
}else if(i *i == x){
pre = i;
break;
}else
pre = i;
}
return pre;
}
int mySqrt_erfen(int x) {
//另解:二分查找:log(n)
int l = 0, h = x, ans = -1;
while(l <= h){
int mid = l + ((h - l) >> 1);
if((long long) mid * mid <= x){
ans = mid;
l = mid + 1;
}else{
h = mid - 1;
}
}
return ans;
}
int mySqrt_netwon_solve(int x) {
//牛顿迭代求解:x_(n+1) = x_n - f(x_n) / f'(x_n)
//原函数为f(a) = a ^ 2 - x
if(x == 0 || x == 1)
return x;
double b0 = x, b1=x;
do{
b0 = b1;
b1 = b0 - (pow(b0, 2) - x) / (2*b0);
// cout << b0 << " " << b1 << endl;
}while(fabs(b0-b1) >= 1e-9);
return (int)b1;
}
};
8. 字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:本题中的空白字符只包括空格字符 ' ' 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例 1:
输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"42"(读入 "42")
^
解析得到整数 42 。
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。
示例 2:输入:s = " -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 "42")
^
解析得到整数 -42 。
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
示例 3:输入:s = "4193 with words"
输出:4193
解释:
第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193 。
由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
int myAtoi(string s) {
//另解参考官方题解:有穷自动机
long long ans = 0;
bool flag = true;
int i = 0;
while(i < s.length() && s[i] == ' '){
i++;
}
if(s[i] == '-'){
flag = false;
i++;
}
else if(s[i] == '+'){
flag = true;
i++;
}
while(i < s.length() && s[i] >= '0' && s[i] <= '9' &&(ans >= -1 * pow(2, 31) && ans <= pow(2, 31) - 1)){
ans = ans * 10 + s[i] - '0';
i++;
}
if(!flag)
ans = -1 * ans;
if(ans > pow(2, 31) - 1)
return pow(2, 31) - 1;
else if(ans < -1 * pow(2, 31))
return -1 * pow(2, 31);
else
return ans;
}
};
另外官方题解中的有穷自动机也是一种值得参考借鉴的思路:
class Automaton {
string state = "start";
unordered_map<string, vector<string>> table = {
{"start", {"start", "signed", "in_number", "end"}},
{"signed", {"end", "end", "in_number", "end"}},
{"in_number", {"end", "end", "in_number", "end"}},
{"end", {"end", "end", "end", "end"}}
};
int get_col(char c) {
if (isspace(c)) return 0;
if (c == '+' or c == '-') return 1;
if (isdigit(c)) return 2;
return 3;
}
public:
int sign = 1;
long long ans = 0;
void get(char c) {
state = table[state][get_col(c)];
if (state == "in_number") {
ans = ans * 10 + c - '0';
ans = sign == 1 ? min(ans, (long long)INT_MAX) : min(ans, -(long long)INT_MIN);
}
else if (state == "signed")
sign = c == '+' ? 1 : -1;
}
};
class Solution {
public:
int myAtoi(string str) {
Automaton automaton;
for (char c : str)
automaton.get(c);
return automaton.sign * automaton.ans;
}
};
43. 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:输入: num1 = "123", num2 = "456"
输出: "56088"
提示:
1 <= num1.length, num2.length <= 200
num1 和 num2 只能由数字组成。
num1 和 num2 都不包含任何前导零,除了数字0本身。
class Solution {
public:
void compute_jinwei(string &ans, int i, int t){
//对index位置的结果进行计算并存储
int index = i, temp = t;
temp += ans[index] - '0';
while(temp){
ans[index] = '0' + temp % 10;
temp = ans[index+1] - '0' + temp / 10;
index++;
}
}
string multiply(string num1, string num2) {
string ans = "";
ans.resize(num1.length() + num2.length() + 2);//乘积长度为num1.length()+num2.length()
for(int i = 0; i < ans.length(); i++)
ans[i] = '0';
reverse(num1.begin(), num1.end());//对字符串num1进行翻转
reverse(num2.begin(), num2.end());//对字符串num2进行翻转
for(int i = 0; i < num1.length(); i++)
for(int j = 0; j < num2.length(); j++){
int temp, index;
index = i + j;//存放结果的索引处
temp = (num1[i] - '0') * (num2[j] - '0');//计算临时结果
compute_jinwei(ans, index, temp);
}
int p = ans.length()- 1 ;
while(p >= 1 && ans[p] == '0'){
//找出不为0的部分
p--;
}
ans.resize(p+1);
reverse(ans.begin(), ans.end());
return ans;
}
};
227. 基本计算器 II
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
示例 1:
输入:s = "3+2*2"
输出:7
示例 2:输入:s = " 3/2 "
输出:1
示例 3:输入:s = " 3+5 / 2 "
输出:5
提示:
1 <= s.length <= 3 * 105
s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数
class Solution {
public:
map<char, int> ch_index;
void init_char_index(){
ch_index['+'] = 0;
ch_index['-'] = 1;
ch_index['*'] = 2;
ch_index['/'] = 3;
ch_index['('] = 4;
ch_index[')'] = 5;
ch_index['#'] = 6;
}
vector<vector<char>> prio;
void init_prio(){
prio = {{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'<', '<', '<', '<', '<', '=', '*'},
{'>', '>', '>', '>', '*', '>', '>'},
{'<', '<', '<', '<', '<', '*', '='}
};
}
int compute(int a, int b, char c){
int sum;
switch(c){
case '+': sum = a + b;break;
case '-': sum = a - b;break;
case '*': sum = a * b;break;
case '/': sum = a / b;break;
}
return sum;
}
int calculate(string s) {
/*
用栈实现:构造运算符之间的优先级别关系
+ - * / ( ) #
*/
stack<int> st_num;
stack<char> st_char;
st_char.push('#');
int i = 0, num = 0;
init_char_index();//对字符对应的下标进行初始化
init_prio();//对优先级进行初始化
while(i < s.length() || st_char.top() != '#'){
//没有把字符串扫描完或者没有把符号栈弹完
if(s[i] >= '0' && s[i] <= '9'){
num = 0;
while(s[i] >= '0' && s[i] <= '9'){
// cout << s[i] << "------";
num = num * 10 + (s[i] - '0');
// cout << num << "----" << s[i] << endl;
i++;
}
st_num.push(num);
}else if(s[i] == ' '){
i++;
}
else{
char ch = prio[ch_index[st_char.top()]][ch_index[s[i]]];
// cout << st_char.top() << ch << s[i] << endl;
if(ch == '>'){
char yunsun = st_char.top();
st_char.pop();
int second = st_num.top();
st_num.pop();
int first = st_num.top();
st_num.pop();
int res = compute(first, second, yunsun);
st_num.push(res);
}else if(ch == '='){
//两个左右括号相遇,括号进行出栈,符号栈不用压栈
st_char.pop();
i++;
}else{
//若为小于关系,直接压入栈即可
st_char.push(s[i++]);
}
}
}
return st_num.top();
}
};
179. 最大数
给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
示例 1:
输入:nums = [10,2]
输出:"210"
示例 2:输入:nums = [3,30,34,5,9]
输出:"9534330"
class Solution {
public:
static bool compare(const int &a, const int &b){
//按降序排序
long la = 10, lb = 10;
while(la <= a){
la *= 10;
}
while(lb <= b){
lb *= 10;
}
//ab和ba的大小比较
return a * lb + b > b * la + a;
}
string largestNumber(vector<int>& nums) {
/*
实际上是对数字进行排序,排序规则和一般的思路不一样
不相同的,按照大小比较
相同数字,比较大小会稍微麻烦一点
*/
sort(nums.begin(), nums.end(), compare);
string ans = "";
for(auto n: nums){
ans += to_string(n);
}
return ans;
}
};
498. 对角线遍历
给你一个大小为 m x n 的矩阵 mat ,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。
示例 1:
输入:mat = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,4,7,5,3,6,8,9]
示例 2:输入:mat = [[1,2],[3,4]]
输出:[1,2,3,4]
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 104
1 <= m * n <= 104
-105 <= mat[i][j] <= 105
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
int m = mat.size(), n = mat[0].size();
vector<int> ans;
bool flag = true;
for(int i = 0; i < m + n - 1; i++){
int a = i < m - 1 ? i : m - 1;
int b = i - a;
int len = ans.size();
while(a >= 0 && b < n){
if(flag)
ans.push_back(mat[a][b]);
else
ans.insert(ans.begin() + len, mat[a][b]);
a--;
b++;
}
flag = !flag;
}
return ans;
}
};
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
class CQueue {
public:
stack<int> st1;
stack<int> st2;
CQueue() {
//此解法在弹出后每次要把原始栈st1恢复,但根据题意可知:st1只用于插入、st2只用于删除,所以st1不用复原
}
void appendTail(int value) {
st1.push(value);
}
int deleteHead() {
int res = -1;
if(st1.empty())
return -1;
int count = 0;
int n = st1.size();
while(!st1.empty()){
count++;
if(count != n){
st2.push(st1.top());
}
res = st1.top();
st1.pop();
}
while(!st2.empty()){
st1.push(st2.top());
st2.pop();
}
return res;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
50. Pow(x, n)
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:输入:x = 2.10000, n = 3
输出:9.26100
示例 3:输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
double x_pow(double x, long long k){
if(k == 0)
return 1;
long long res = k % 2;
double out = x_pow(x, k/2);
if(res)
return out * out * x;
else
return out * out;
}
double myPow(double x, int n) {
//递归法+迭代法
long long k = n;
return k > 0 ? x_pow(x, k) : 1.0 / x_pow(x, -1 * k);
}
double myPow_diedai(double x, int n) {
//递归法+迭代法
//迭代法:用n的二进制表示来代替
double res = 1.0, con = x;
long long N = abs(n);
while(N){
if(N % 2 == 1){
//当前位为1
res *= con;
}
N /= 2;//
con *= con;//贡献翻倍
}
if(n >= 0)
return res;
else
return 1.0 / res;
}
};
440. 字典序的第K小数字
给定整数 n 和 k,返回 [1, n] 中字典序第 k 小的数字。
示例 1:
输入: n = 13, k = 2
输出: 10
解释: 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。
示例 2:输入: n = 1, k = 1
输出: 1
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
long long compute_pre_num(long long x, long long n){
//以x为前缀构成的数的个数
long long first = x, last = x, count = 0;
while(first <= n){
count += (min(last, n) - first + 1);
first = first * 10;
last = last * 10 + 9;
}
return count;
}
long long compute_last(long long x, long long n){
//以x为前缀的最后一个元素
long long first = x, last = x, pre_first, pre_last;
while(first <= n){
pre_first = min(first, n);
pre_last = min(last, n);
first = first * 10;
last = last * 10 + 9;
}
return max(pre_first, pre_last);
}
int findKthNumber(int n, int k) {
//字典树的思想:建立十叉树,运用先序遍历即可
//遇到某个节点所表示的值x,判断以该节点作为根节点的树的节点数目还能不能到k
int ans = 1;
while(k > 1){
long long count = compute_pre_num(ans, n);
// cout << ans << "---" << count << "***" << k << endl;
if(count < k){
//不在当前节点下,移动到相邻节点上
ans += 1;
k -= count;
}else if(count > k){
//在当前节点下,下移到左边第一个节点
ans *= 10;
k--;
}else{
//count = k,说明正好对应到当前子树的最后一个节点
k -= count;
ans = compute_last(ans, n);
}
}
return ans;
}
};
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
#include "bits/stdc++.h"
using namespace std;
class MyQueue {
public:
stack<int> st1, st2;//s1是最终的结果,s2是中转实现的栈
MyQueue() {
}
void push(int x) {
while(!st1.empty()){
//将st1中的元素逐个加入到st2中
st2.push(st1.top());
st1.pop();
}
st1.push(x);
while(!st2.empty()){
st1.push(st2.top());
st2.pop();
}
}
int pop() {
int ans = st1.top();
st1.pop();
return ans;
}
int peek() {
return st1.top();
}
bool empty() {
return st1.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:输入:nums = [3,3], target = 6
输出:[0,1]
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//使用哈希表存储的是元素的位置
unordered_map<int, int> num_index;
for(int i = 0; i < nums.size(); i++){
int part = target - nums[i];
if(num_index.find(part) != num_index.end()){
return {num_index[part], i};
}
num_index[nums[i]] = i;
}
return {-1, -1};
}
};
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:输入: nums = [1], k = 1
输出: [1]
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int, int> val_num;
for(int i = 0; i < nums.size(); i++){
// map<int, int>::iterator it = val_num.find(nums[i]);
// if(it != val_num.end())
val_num[nums[i]] += 1;
// else
// val_num[nums[i]] = 1;
}
map<int, int>::iterator it = val_num.begin();
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;//升序队列,优先级高的后出队
while(it != val_num.end()){
while(q.size() < k){
q.push(make_pair(it->second, it->first));//数量,值
it++;
}
if(it->second > q.top().first){
q.pop();
q.push(make_pair(it->second, it->first));
}
it++;
}
vector<int> vec;
while(!q.empty()){
cout << q.top().second << endl;
vec.push_back(q.top().second);
q.pop();
}
return vec;
}
};
int main(){
vector<int> nums{1,1,1,2,2,3};
int k = 2;
Solution sol;
sol.topKFrequent(nums, k);
}