力扣019. 删除链表的倒数第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 != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
作者:carlsun-2
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/19-shan-chu-lian-biao-de-dao-shu-di-nge-jie-dia-85/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 22. 链表中倒数第k个节点
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* fast = head;
ListNode* slow = head;
while(k--) {
fast = fast->next;
}
while(fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
牛客NC69 链表中倒数第K个节点
输入一个链表,输出该链表中倒数第k个结点。
思路:
设置两个指针 fast 、slow,fast先走k步,如果走不到第k步(nullptr节点是可以走到的,但是nullptr节点没有next,所以只能走到nullptr),说明链表长度不够k,直接返回nullptr;然后,令 fast 和 slow 开始同步往下移动,直到 fast 移动到nullptr,此时slow就是倒数第 k 个节点的,返回即可。
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr || k < 1){
return nullptr;
}
ListNode *pre = pListHead;
ListNode *last = pListHead;
while(k > 0){
if(last == nullptr){
return nullptr;
}
last = last->next;
k--;
}
while(last != nullptr){
last = last->next;
pre = pre->next;
}
return pre;
}
};
力扣086. 分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
if(!head) return nullptr;
ListNode* smaller_head = new ListNode(985); // 定义一个新的链表,用来存比x小的值
ListNode* smaller = smaller_head; // 定义一个指针,初始位置指在上面的新链表的头结点
ListNode* larger_head = new ListNode(211); // 定义一个新的链表,用来存比x大或等于x的值
ListNode* larger = larger_head; // 定义一个指针,初始位置指在上面的新链表的头结点
while(head)
{
if(head -> val < x)
{
smaller -> next = head;
smaller = smaller -> next;
}
else
{
larger -> next = head;
larger = larger -> next;
}
head = head -> next;
}
larger -> next = nullptr;
smaller -> next = larger_head -> next; // 由此可得,larger_head的头结点,其实是一个dummy值(哑值)
return smaller_head -> next; // 由此可得,smaller_head的头结点,其实是一个dummy值(哑值)
}
};
作者:superkakayong
链接:https://leetcode-cn.com/problems/partition-list/solution/zi-jie-ti-ku-86-zhong-deng-fen-ge-lian-biao-partit/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣141.环形链表
fast和slow各自再走一步, fast和slow就相遇了
这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,说明有环
if (slow == fast) return true;
}
return false;
}
};
作者:carlsun-2
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-shuang-zhi-zhen-zhao-huan-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣142. 环形链表 II
主要考察两知识点:
1.判断链表是否环
2.如果有环,如何找到这个环的入口
那么相遇时:
slow指针走过的节点数为: x + y
fast指针走过的节点数: x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为我们要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以我们要求x ,将x单独放在左面:x = n (y + z) - y
在从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针
先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。当 n为1的时候,公式就化解为 x = z.
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点.
/**
* 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) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};
作者:carlsun-2
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/142-huan-xing-lian-biao-ii-jian-hua-gong-shi-jia-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣287. 寻找重复数
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
思路同上一题:
如上图,slow和fast会在环中相遇,先假设一些量:起点到环的入口长度为m,环的周长为c,在fast和slow相遇时slow走了n步。则fast走了2n步,fast比slow多走了n步,而这n步全用在了在环里循环(n%c==0)。
当fast和last相遇之后,我们设置第三个指针finder,它从起点开始和slow(在fast和slow相遇处)同步前进,当finder和slow相遇时,就是在环的入口处相遇,也就是重复的那个数字相遇。
为什么 finder 和 slow 相遇在入口
fast 和 slow 相遇时,slow 在环中行进的距离是n-m,其中 n%c== 0。这时我们再让 slow 前进 m 步——也就是在环中走了 n 步了。而 n%c==0 即 slow 在环里面走的距离是环的周长的整数倍,就回到了环的入口了,而入口就是重复的数字。
我们不知道起点到入口的长度m,所以弄个 finder 和 slow 一起走,他们必定会在入口处相遇。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast = 0, slow = 0;
while(true){
fast = nums[nums[fast]];
slow = nums[slow];
if(fast == slow)
break;
}
int finder = 0;
while(true){
finder = nums[finder];
slow = nums[slow];
if(slow == finder)
break;
}
return slow;
}
};
作者:zjczxz
链接:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/kuai-man-zhi-zhen-de-jie-shi-cong-damien_undoxie-d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣075. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
思路:
注意:为什么当nums[i]==0的时候为什么不需要循环判断,因为这个时候nums[p0]交换过去的值肯定是1,因为开始的时候i和p0是一起走的,只有nums[i]==1时,才会跳过去,而这时候p2指向的是第一个1.
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p2 = n - 1;
for (int i = 0; i <= p2; ++i) {
while (i <= p2 && nums[i] == 2) {
swap(nums[i], nums[p2]);
--p2;
}
if (nums[i] == 0) {
swap(nums[i], nums[p0]);
++p0;
}
}
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sort-colors/solution/yan-se-fen-lei-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣581. 最短无序连续子数组
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
思路:
很简单,如果最右端的一部分已经排好序,这部分的每个数都比它左边的最大值要大,同理,如果最左端的一部分排好序,这每个数都比它右边的最小值小。所以我们从左往右遍历,如果i位置上的数比它左边部分最大值小,则这个数肯定要排序, 就这样找到右端不用排序的部分,同理找到左端不用排序的部分,它们之间就是需要排序的部分
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int left=0;int right=nums.size();//这里right为i不可能取到的值,这样是防止那种递增数组
int maxtemp=INT_MIN; int mintemp=INT_MAX;
for(int i=0;i<nums.size();i++)
{
if(nums[i]<maxtemp) right=i;
maxtemp=max(maxtemp,nums[i]);
}
for(int i=nums.size()-1;i>=0;i--)
{
if(nums[i]>mintemp) left=i;
mintemp=min(mintemp,nums[i]);
}
return right-left==nums.size()? 0:right-left+1;
}
};
力扣剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
思路:
滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。
对于这道题来说,数组就是正整数序列 [1, 2, 3, …, n]。我们设滑动窗口的左边界为 i,右边界为 j,则滑动窗口框起来的是一个左闭右开区间 [i, j)。注意,为了编程的方便,滑动窗口一般表示成一个左闭右开区间。在一开始i=1,j=1,滑动窗口位于序列的最左侧,窗口大小为零。
vector<vector<int>> findContinuousSequence(int target) {
int i = 1; // 滑动窗口的左边界
int j = 1; // 滑动窗口的右边界
int sum = 0; // 滑动窗口中数字的和
vector<vector<int>> res;
while (i <= target / 2) {
if (sum < target) {
// 右边界向右移动
sum += j;
j++;
} else if (sum > target) {
// 左边界向右移动
sum -= i;
i++;
} else {
// 记录结果
vector<int> arr;
for (int k = i; k < j; k++) {
arr.push_back(k);
}
res.push_back(arr);
// 左边界向右移动
sum -= i;
i++;
}
}
return res;
}
作者:nettee
链接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣151. 翻转字符串里的单词
给定一个字符串,逐个翻转字符串中的每个单词。
说明:
无空格字符构成一个 单词 。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
思路:双指针+栈
class Solution {
public:
string reverseWords(string s) {
if(s.size()<2)return s;
stack<string> st;
int size=s.size(),i=0;
string cur="";
while(i<size){
if(s[i]!=' '){
int j=i;
while(s[j]!=' '&&j<size){
j++;
}
st.push(s.substr(i,j-i));
i=j;
}
i++;
}`
//依次弹出单词,然后合并,注意,该合并完成的字符串尾部有个空格。
while(!st.empty()){
cur+=st.top()+" ";
st.pop();
}
//删去空格
cur.pop_back();
return cur;
}
};
力扣125. 验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
思路:
大写的A 是65,小写的a 是97,
字母依次增大,号码也相应增大,
相互之间需要转换的话,加或减去他们的差值就可以了,
也就是大写变小写的话,就+32
记一笔 c++的几个内置函数
islower(char c) 是否为小写字母
isupper(char c) 是否为大写字母
isdigit(char c) 是否为数字
isalpha(char c) 是否为字母
isalnum(char c) 是否为字母或者数字
toupper(char c) 字母小转大
tolower(char c) 字母大转小
class Solution {
public:
bool isPalindrome(string s) {
string tmp;
for (auto c : s) {
if (islower(c) || isdigit(c)) tmp += c;
else if (isupper(c)) tmp += (c + 32);
}
int i = 0, j = tmp.size() - 1;
while (i < j) {
if (tmp[i] != tmp[j]) return false;
i++;
j--;
}
return true;
}
};
作者:karlzhang
链接:https://leetcode-cn.com/problems/valid-palindrome/solution/9877-ci-ti-mu-de-shou-xi-7ge-zi-mu-shu-zi-pan-duan/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。