方法总结
1.哈希映射
数组:确定数量
集合:去重
字典:需要同时处理value和下标
2.双指针
left/right向中间夹逼
low/fast快慢指针向一个方向移动
时间尺度上,right指向的是目标(结果)数组,letf指向的是当前(待处理)数组,参考移除元素
3.滑动窗口
left/right指针,当窗口内满足条件时操作左右指针
3.二分查找
有序数组,有时间限制
4.求和或者求差
反向思维:求和就是球差、求差就是求和
5.字符串翻转
部分翻转和整体翻转配合
6.单调栈
处理求元素左右第一个最大/最小问题
707. 设计链表
class MyLinkedList {
private:
struct LinkedListNode{
int val;
struct LinkedListNode* next; //这里包括以下不需要struct,跟C语言的区别
LinkedListNode(int val):val(val), next(nullptr){};//默认构造函数,
};
int len;
struct LinkedListNode* head;
public:
MyLinkedList() {
head = new struct LinkedListNode(0);//没有参数出错,已经没有无参数的构造函数
len = 0;
}
int get(int index) {//index从0开始,区别于len
if (index > (len - 1) || index < 0) {
return -1;
}
struct LinkedListNode* cur = head->next;
while(index--){
cur = cur->next;
}
print();
return cur->val;
}
void addAtHead(int val) {
struct LinkedListNode* node = new struct LinkedListNode(val);
node->next = head->next;
head->next = node;
len++;
print();
}
void addAtTail(int val) {
struct LinkedListNode* cur = head;
while(cur->next != nullptr){
cur = cur->next;
}
struct LinkedListNode* node = new struct LinkedListNode(val);
cur->next = node;
len++;
print();
}
void addAtIndex(int index, int val) {
if(index > len) return;
if(index < 0) index = 0;
LinkedListNode* newNode = new LinkedListNode(val);
LinkedListNode* cur = head;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
len++;
print();
}
void deleteAtIndex(int index) {
if(index > len-1 || index < 0)
{
return;
}
struct LinkedListNode* cur = head;
while(index--){
cur = cur->next;
}
struct LinkedListNode* tem = cur->next;
cur->next = cur->next->next;
delete tem;
len--;
print();
}
void print(){
LinkedListNode* cur = head->next;
while(cur != nullptr){
std::cout << cur->val << " ";
cur = cur->next;
}
std::cout << std::endl;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
206. 反转链表
摘取旧链表节点,再用头插法创建
/**
* 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:
ListNode* reverseList(ListNode* head) {
if(head == nullptr) return head;
ListNode* newhead = new ListNode(0);
ListNode* cur = head;
while(cur != nullptr)//cur->next会漏掉最后一个
{
ListNode* tem = cur->next;
cur->next= newhead->next;
newhead->next = cur;
cur = tem;
}
return newhead->next;
}
};
19. 删除链表的倒数第 N 个结点
双指针,fast比slow快N+1步,当fast指向最后一个节点的时候,slow指向待删除节点前一个。
/**
* 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:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* newhead = new ListNode();//需要增加头节点,否则对于head:[2]->null无法处理
newhead->next = head;
ListNode* slow = newhead;
ListNode* fast = newhead;
while(n--){
fast = fast->next;
}
while(fast->next != nullptr){
slow = slow->next;
fast = fast->next;
}
ListNode* tem = slow->next;
slow->next = slow->next->next;
delete tem;
tem = nullptr;
return newhead->next;
}
};
链表相交
计算两条链表长度差,长的那条提前走长度差的步数(尾对齐),同时往前走找交点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//计算长度差
ListNode* cur = headA;
int sizeA = 0;
int sizeB = 0;
while(cur != nullptr){
sizeA++;
cur = cur->next;
}
cur = headB;
while(cur!= nullptr){
sizeB++;
cur = cur->next;
}
// std::cout<<sizeA<< ' '<<sizeB;
//长的链表多走差值的步数
ListNode* curA = headA;
ListNode* curB = headB;
for(int i = abs(sizeA - sizeB); i > 0; i--){
if(sizeA > sizeB){
curA = curA->next;
}
else{
curB = curB->next;
}
}
// std::cout<< curA->val <<' '<<curB->val;
//同时走,当遇到节点地址相同时,表示该节点是交点
while(curA != nullptr){
if(curA == curB){
return curA;
}
curA = curA->next;
curB = curB->next;
}
return nullptr;
}
};
242. 有效的字母异位词
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int> n(26, 0);
for(int i = 0; i < s.size(); i++)
{
n[s[i]-'a']++;
}
for(int i = 0; i < t.size(); i++)
{
n[t[i]-'a']--;
if(n[t[i]-'a'] < 0) return false;
}
for(int i = 0; i < n.size(); i++)
{
if(n[i] != 0) return false;
}
return true;
}
};
349. 两个数组的交集
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result;
unordered_set<int> set;
for(int n : nums1){
set.insert(n);
}
//改进 unordered_set<int> set(nums1.begin(), nums1.end());
for(int n : nums2){
if(set.find(n) != set.end()){
result.insert(n);
}
}
return vector<int>(result.begin(), result.end());
}
};
202. 快乐数
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> set;
while(1){
//sum置零
int sum = 0;
//计算各位置的平方和
while(n != 0){
int tem = n % 10;
n = n / 10;
sum += tem*tem;
}
std::cout << sum << std::endl;
//是快乐数
if(sum == 1) return true;
//重复返回false
if(set.find(sum) != set.end()){
return false;
}
//不重复添加进set
set.insert(sum);
//继续计算
n = sum;
}
return true;
}
};
1. 两数之和
找出map中是否有target-nums[i],
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int i = 0 ;i < nums.size(); i++)
{
if(hash.find(target - nums[i]) != hash.end())
{
return {i, hash[target - nums[i]]};
}
else{
hash[nums[i]] = i;
}
}
return {};
}
};
454. 四数相加 II
先计算nums1+nums2再计算nums3+nums4,最后找和==0,会超时。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int res = 0;
map<int, int> hash;
for(int i : nums1){
for(int j : nums2){
hash[i+j]++;
}
}
for(int i:nums3){
for(int j:nums4){
if(hash.find(0-(i+j)) != hash.end()){
res+= hash[0-(i+j)];
}
}
}
return res;
}
};
383. 赎金信
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
vector<int> hash(26, 0);
for(int i = 0; i < magazine.size(); i++){
hash[magazine[i]-'a']++;
}
for(int i = 0; i < ransomNote.size(); i++){
hash[ransomNote[i]-'a']--;
if(hash[ransomNote[i]-'a'] < 0){
return false;
}
}
return true;
}
};
26. 删除有序数组中的重复项
使用双指针
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int left = 0;
int right = 1;
while(right < nums.size())
{
if(nums[left] == nums[right])
{
right++;//数字相同时,right右移
}
else
{
nums[++left] = nums[right++];//不同时
}
}
return left + 1;
}
};
27. 移除元素
双指针
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int lowIndex =0;
for(int fastIndex = 0; fastIndex < nums.size();fastIndex++)
{
if(nums[fastIndex]!=val)
{
nums[lowIndex++] = nums[fastIndex];
}
}
return lowIndex;
}
};
66. 加一
我写的,能过一半用例,笨方法。只是想确定一下笨方法可不可行,不管笨不笨,能拿到分就是好方法。
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
stack<int> stack;
vector<int> res;
uint num = 0;
uint cont = 1;
for(int i = digits.size()-1;i >= 0;i--)
{
num += digits[i]*cont;
cont *= 10;
}
cout <<num;
num += 1;
while(num!=0)
{
stack.push(num%10);
num = num/10;
}
while(!stack.empty())
{
res.push_back(stack.top());
stack.pop();
}
return res;
}
};
比较好的方法,使用笨方法的原因也是不知道vector可以很方便的在头插入数据。
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
int len = digits.size();
for(int i = len - 1; i >=0; i--)
{
if(++digits[i] < 10)
{
return digits;
}
else
{
digits[i] = digits[i] % 10;
}
}
digits.insert(digits.begin(), 1);
return digits;
}
};
88. 合并两个有序数组
题目没限制就用sort
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
for(int i = 0; i !=n;i++)
{
nums1[m+i] = nums2[i];
}
sort(nums1.begin(), nums1.end());
}
};
由于是数组,不好在nums1上进行操作,既然题目没限制,就用额外空间去存,然后转到nums1上。
我写的
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = 0,j=0;
vector<int> res;
while(i!=m&&j!=n)
{
if(nums1[i] <= nums2[j])
{
res.push_back(nums1[i]);
i++;
}
else
{
res.push_back(nums2[j]);
j++;
}
}
if(i == m)
{
while(j!=n)
{
res.push_back(nums2[j++]);
}
}
else{
while(i!=m)
{
res.push_back(nums1[i++]);
}
}
for (int i = 0; i != m + n; ++i) {
nums1[i] = res[i];
}
}
};
力扣写的
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p1 = 0, p2 = 0;
int sorted[m + n];
int cur;
while (p1 < m || p2 < n) {
if (p1 == m) {
cur = nums2[p2++];
} else if (p2 == n) {
cur = nums1[p1++];
} else if (nums1[p1] < nums2[p2]) {
cur = nums1[p1++];
} else {
cur = nums2[p2++];
}
sorted[p1 + p2 - 1] = cur;
}
for (int i = 0; i != m + n; ++i) {
nums1[i] = sorted[i];
}
}
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/merge-sorted-array/solutions/666608/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1365. 有多少小于当前数字的数字
排序后从后往前,元素所在位置的下标就是第几大
class Solution:
def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
hash = [0]*101
tem = nums[:]
tem.sort()
for index in range(len(tem)-1,-1,-1): #注意从后往前
hash[tem[index]] = index
# res = [0]*len(nums)
for i in range(len(nums)-1,-1,-1):
nums[i] = hash[nums[i]]
return nums
941. 有效的山脉数组
class Solution:
def validMountainArray(self, arr: List[int]) -> bool:
left = 0
right = len(arr) -1
while left<len(arr)-1 and arr[left+1]>arr[left]:
left += 1
while right>0 and arr[right-1] > arr[right]:
right -= 1
if left == right and left != 0 and right != len(arr)-1:
return True
else:
return False
1207. 独一无二的出现次数
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
hash = dict()
for i in range(len(arr)):
hash[arr[i]] = hash.get(arr[i], 0) + 1
value = sorted(hash.values())
for i in range(len(value)-1):
if(value[i] == value[i+1]):
return False
return True
189. 轮转数组
翻转字符串相似,先整体翻转,再两个部分翻转
注意:k可能大于串长,需要取余操作
class Solution {
public:
void rotate(vector<int>& nums, int k) {
k = k % nums.size();
reverse(nums.begin(), nums.end()); #必须先翻转整个
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
};
python不支持list部分翻转,没写翻转法
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k = k%len(nums)
nums[:] = nums[-k:] + nums[0:-k] #这里必须是nums[:]对原列表进行修改
724. 寻找数组的中心下标
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
s = sum(nums)
leftsum = 0
rightsum = 0
for i in range(len(nums)):
leftsum += nums[i] #多一个nums[i]
rightsum = s - leftsum + nums[i] #多加一个nums[i]与左边保持相等
if leftsum == rightsum:
return i
return -1
922. 按奇偶排序数组 II
哈希+双指针,一趟遍历可以结束
class Solution:
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
res = [0]*len(nums)
odd = 1
even = 0
index = 0
while odd <len(nums) or even < len(nums):
tem = nums[index]%2
if tem == 0:
res[even] = nums[index]
even += 2
index += 1
elif tem == 1:
res[odd] = nums[index]
odd += 2
index += 1
return res
35. 搜索插入位置
插入位置为left
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
length = len(nums)
left = 0
right = length - 1
while left <= right:
mid = int(left + (right - left)/2)
if target > nums[mid]:
left = mid + 1
elif target < nums[mid]:
right = mid - 1
else:
return mid
return left
744. 寻找比目标字母大的最小字母
class Solution {
public:
char nextGreatestLetter(vector<char>& letters, char target) {
if(target >= letters[letters.size() - 1])
return letters[0];
int low = 0;
int mid;
int high = letters.size()-1;
while(low <= high)
{
mid = (low+high)/2;
if(target > letters[mid])
{
low = mid + 1;
}
else if(target < letters[mid])
{
high = mid -1;
}else{
std::cout<< mid;
while(letters[mid] == letters[mid + 1]) mid++;//多个相同时可能会定位在中间
return letters[mid + 1];
}
}
return letters[high + 1];
}
};
单调栈
739. 每日温度
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
stack = [0]
res = [0] * len(temperatures)
for i in range(1, len(temperatures)):
while len(stack)>0 and temperatures[i] > temperatures[stack[-1]]:
res[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return res
496. 下一个更大元素 I
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
res = [-1]*len(nums1)
# print(nums2.index(1))
for index, num in enumerate(nums1):
for i in nums2[nums2.index(num)+1:]:
if i > num:
res[index] = i
break
return res
503. 下一个更大元素 II
可以把两个数组拼一块
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
42. 接雨水
class Solution:
def trap(self, height: List[int]) -> int:
# 单调栈
'''
单调栈是按照 行 的方向来计算雨水
从栈顶到栈底的顺序:从小到大
通过三个元素来接水:栈顶,栈顶的下一个元素,以及即将入栈的元素
雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度
雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度)
'''
# stack储存index,用于计算对应的柱子高度
stack = [0]
result = 0
for i in range(1, len(height)):
# 情况一
if height[i] < height[stack[-1]]:
stack.append(i)
# 情况二
# 当当前柱子高度和栈顶一致时,左边的一个是不可能存放雨水的,所以保留右侧新柱子
# 需要使用最右边的柱子来计算宽度
elif height[i] == height[stack[-1]]:
stack.pop()
stack.append(i)
# 情况三
else:
# 抛出所有较低的柱子
while stack and height[i] > height[stack[-1]]:
# 栈顶就是中间的柱子:储水槽,就是凹槽的地步
mid_height = height[stack[-1]]
stack.pop()
if stack:
right_height = height[i]
left_height = height[stack[-1]]
# 两侧的较矮一方的高度 - 凹槽底部高度
h = min(right_height, left_height) - mid_height
# 凹槽右侧下标 - 凹槽左侧下标 - 1: 只求中间宽度
w = i - stack[-1] - 1
# 体积:高乘宽
result += h * w
stack.append(i)
return result
205. 同构字符串
判断字符是否相互映射,一旦发现当前映射关系与之前的错误就返回False
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
dict = {}
for i in range(len(s)):
if dict.get(s[i]) == None:
if t[i] not in dict.values():
dict[s[i]] = t[i]
else:
return False
else:
if dict[s[i]] != t[i]:
return False
return True
925. 长按键入
class Solution:
def isLongPressedName(self, name: str, typed: str) -> bool:
i = 0
j = 0
while j<len(typed):
if i < len(name) and name[i] == typed[j]:
i += 1
j += 1
elif j >0 and typed[j] == typed[j-1]:
j += 1
else:
return False
if i == len(name):
return True
else:
return False
844. 比较含退格的字符串
使用栈 ,没有做精简
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
stack1 = []
stack2 = []
i = 0
while i<len(s):
if s[i] != '#':
stack1.append(s[i])
elif s[i] == '#':
if len(stack1)>0:
stack1.pop()
else:
i += 1
continue
i+=1
i = 0
while i<len(t):
if t[i] != '#':
stack2.append(t[i])
elif t[i] == '#':
if len(stack2)>0:
stack2.pop()
else:
i+=1
continue
i+=1
if stack1[:] == stack2[:]:
return True
return False