双指针
数组
leetcode: 344 反转字符串
原地移除数组上的元素,数组上的元素,不能真正的删除,因此只能覆盖。下述代码中通过两个前后指针指向前端和末尾,在一个for循环下实现转换。
class Solution {
public:
void reverseString(vector<char>& s) {
for (int i = 0; i < s.size()/2; i++) {
swap(s[i],s[s.size() - i - 1]);
}
}
};
leetcode: 977 有序数组的平方
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
vector<int> result(nums.size(), 0);
int k = nums.size() - 1;
while(left <= right){
if(abs(nums[left]) > abs(nums[right])){
result[k--] = nums[left] * nums[left];
left++;
}
else{
result[k--] = nums[right] * nums[right];
right--;
}
}
return result;
}
};
Leetcode: 209 长度最小的子数组
滑动窗,实际上相当于双指针
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0, right = 0;
int result = INT32_MAX;
int sum = 0;
for(int right = 0; right < nums.size();right++){
sum += nums[right];
while(sum >= target){//特别注意这是是while,不是if,因为要左边缩小到最小
int length = right - left + 1;
if(length < result) result = length;
sum -= nums[left];
left++;
}
}
if(result == INT32_MAX) return false;
return result;
}
};
字符串
Leetcode: 27 移除元素
定义两个快慢指针,快指针指向跳过位置的下一个位置。慢指针用于对元素的赋值。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.size(); fast++){
if(nums[fast] != val){
nums[slow++] = nums[fast];
}
}
return slow;
}
};
Leetcode: 151 反转字符串中的单词
分单词的反转。
- 删除字符串中额外的空格进行处理
- 整体字符串反转
- 遇到空格的话,小的字符反转
class Solution {
public:
void reverse(string& s, int start, int end){//定义反转的函数
for(int i = start, j = end; i < j; i++, j--){
swap(s[i], s[j]);
}
}
void removezero(string& s){//定义删除空格的操作,使用了双指针
int start = 0;
for(int fast = 0; fast < s.size(); fast++){
if(start != 0 && s[fast] != ' ') s[start++] = ' ';//如果在非字符首遇到不是空格的元素,说明是单词的开头,在前面加上一个空格
while(fast < s.size() && s[fast] != ' '){
s[start++] = s[fast++];//如果快指针没指到最后,并且没有遇到空格,复制单词
}
}
s.resize(start);//字符串长度resize
}
string reverseWords(string s) {
removezero(s);
reverse(s, 0, s.size() - 1);
int start = 0;
for(int i = 0; i <= s.size(); ++i){//注意是左闭右闭区间
if(i == s.size() || s[i] == ' '){//如果当前i元素等于‘ ’,那么就反转到i-1
reverse(s, start, i - 1);
start = i + 1;//从i后面一个元素开始
}
}
return s;
}
};
链表
Leetcode: 19 删除链表的倒数第N个节点
定义快慢指针来删除
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* slow = dummyhead;
ListNode* fast = dummyhead;
while(n--){
fast = fast->next;
}
while(fast->next != NULL){
slow = slow->next;
fast = fast->next;
}
slow->next = slow->next->next;
return dummyhead->next;
}
};
Leetcode: 206反转链表
定义两个指针,pre和cur,其中temp用于保存断开链表之后的cur-next。
pre和cur一前一后,先将cur-next保存好,然后将pre连接到cur-next上,将pre箱右移动,cur向右移动。
/**
* 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) {
ListNode* pre = NULL;
ListNode* cur = head;
ListNode* temp;
while(cur){
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
Leetcode: 142环形链表
设计快慢指针,low走一步和fast走两步,如果两个指针相遇了,说明链表有环,不然快指针会一直比慢指针快。
找到环的入口位置,也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是环形入口的节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
ListNode* index1 = fast;
ListNode* index2 = head;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index2;
}
}
return NULL;
}
};
N数之和
Leetcode: 15 3数之和
这里涉及到双指针和相关的去重操作,去重的地方设置很重要。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size(); i++){
if(nums[i] > 0) return result;
if(i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.size() - 1;
while(right > left){
if(nums[i] + nums[right] + nums[left] > 0) right--;
else if(nums[i] + nums[right] + nums[left] < 0) left++;
else{
result.push_back(vector<int>{nums[i], nums[right], nums[left]});
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
left++;
right--;
}
}
}
return result;
}
};
Leetcode: 18 4数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
如果N数之和要返回下标的话,就不能使用双指针法,得尝试使用哈希表进行操作了。
- 数组、字符串、链表、哈希表
链表
哈希表
Leetcode: 242 有效的字母异位词
这题的hash表,是以数组实现的,因为字母只有26个,所以只需要26个数字的数组。
class Solution {
public:
bool isAnagram(string s, string t) {
int count[26] = {0};
for(int i = 0; i < s.size(); i++){
count[s[i] - 'a']++;
}
for(int i = 0; i < t.size(); i++){
count[t[i] - 'a']--;
}
for(int i = 0; i < 26; i++){
if(count[i] != 0) return false;
}
return true;
}
};
Leetcode: 349 两个数组的交集
注意点在于去重结果的时候可以巧用unordered_set<int>来去重,同时最后返回结果需要将set转化为vector。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> count(nums1.begin(), nums1.end());
unordered_set<int> result;//用于存放结果去重
for(int i = 0; i < nums2.size(); i++){
if(count.find(nums2[i]) != count.end()){
result.insert(nums2[i]);
}
}
return vector<int>(result.begin(), result.end());
}
};
Leetcode: 202 快乐数
class Solution {
public:
// 取数值各个位上的单数之和
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);//平方
n /= 10;//求每个位数
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while(1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
// 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};
Leetcode: 1 两数之和
因为本题,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> map;
for(int i = 0; i < nums.size(); i++){
int s = target - nums[i];
auto count = map.find(s);
if(count != map.end()){
return {count->second,i};
}
map.insert(pair<int, int>(nums[i],i));
}
return {};
}
};
Leetcode: 454 四数相加
- 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
- 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
- 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
- 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
- 最后返回统计值 count 就可以了
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : A) {
for (int b : B) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
Leetcode: 383 赎金信
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
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']--;
}
for(int i = 0; i < 26; i++){
if(hash[i] < 0) return false;
}
return true;
}
};
数组
Leetcode: 704 二分查找
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == target){
return mid;
}
else if(nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return -1;
}
};
Leetcode: 59 螺旋矩阵
主要注意代码的区间写法,很容易出现错误,只能记住了这道题。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
链表
acm格式中手写链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
链表基本操作
Leetcode: 203移除链表元素
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead;
while(cur ->next != NULL){
if(cur->next ->val == val){
cur->next = cur->next->next;
}
else{
cur = cur->next;
}
}
return dummyhead->next;
}
};
需要注意虚拟头节点的使用。
Leetcode: 707 设计链表
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
// 初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){ // 如果--index 就会陷入死循环
cur = cur->next;
}
return cur->val;
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
_size--;
}
// 打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
Leetcode:206 反转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* newhead = NULL;
ListNode* cur = head;
while(cur){
ListNode * node = cur->next;
cur->next = newhead;
newhead = cur;
cur = node;
}
return newhead;
}
};
设计一个节点来保存当节点的下一个。
Leetcode:24 两两交换链表中的节点
/**
* 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* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead;
while(cur->next != NULL && cur->next->next != NULL){
ListNode* node = cur->next;
ListNode* node1 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = node;
cur->next->next->next = node1;
cur = cur->next->next;
}
return dummyhead->next;
}
};
这道题需要两个中间节点来保存指针。
Leetcode: 链表相交
第一种方案更加简单,设计数学追击问题,很巧妙,carl的方法复杂了。
/**
* 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) {
if(headA == NULL || headB == NULL)
return NULL;
ListNode* curA = headA;
ListNode* curB = headB;
while(curA != curB){
if(curA == NULL){
curA = headB;
}
else{
curA = curA->next;
}
if(curB == NULL){
curB = headA;
}
else{
curB = curB->next;
}
}
return curA;
}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};