文章目录
双指针
leetcode 27. 移除元素
leetcode 27. 移除元素
给定一个数组,要求原地删除元素值为target的元素,不能使用额外的空间;
那么维护一个快指针fastIndex,慢指针slowIndex:
- nums[fastIndex] == target时,fastIndex++;
- nums[fastIndex] != target时,fastIndex++, slowIndex++;
代码如下:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
int removeElement(int* nums, int numsSize, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < numsSize; fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
暴力解如下:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( 1 ) O(1) O(1)
int removeElement(int* nums, int numsSize, int val) {
int size = numsSize;
for (int i = 0; i < size ; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
leetcode 344.反转字符串(左右双指针)
leetcode 344.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
- 输入:[“h”,“e”,“l”,“l”,“o”]
- 输出:[“o”,“l”,“l”,“e”,“h”]
使用双指针即可
C语言代码:
void reverseString(char* s, int sSize){
int left = 0, right = sSize - 1;
while (left < right) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
}
其中交换swap有两种实现方式:
int tmp = s[i];
s[i] = s[j];
s[j] = tmp;
一种就是通过位运算:
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
leetcode 剑指 Offer 05. 替换空格(左右双指针)
leetcode 剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
- 输入:s = “We are happy.”
- 输出:“We%20are%20happy.”
C语言代码:
在原有的空间上增加空间,不需要额外分配空间
char* replaceSpace(char* s){
int count = 0, len = 0;
for (int i = 0; i < strlen(s); i++) {
len++;
if (s[i] == ' ') {
count++;
}
}
s = (char*)realloc(s, sizeof(char) * (len + 2 * count + 1));
s[len + 2 * count] = '\0';
int R = len + 2 * count - 1;
for (int L = len - 1; L >= 0; L--) {
if (s[L] != ' ') {
s[R--] = s[L];
} else {
s[R--] = '0';
s[R--] = '2';
s[R--] = '%';
}
}
return s;
}
leetcode 151. 翻转字符串里的单词
leetcode 151. 翻转字符串里的单词
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
- 输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
示例 1:
- 输入:s = “the sky is blue”
- 输出:“blue is sky the”
此题是将整个单词看作整体,再将其进行翻转
C语言代码:
char * reverseWords(char * s) {
int sLen = strlen(s);
char* res = (char*)calloc(sLen + 1, sizeof(char));
int pre = sLen - 1, index = 0;
while (pre >= 0) {
if (s[pre] == ' ') {
pre--;
} else {
int after = pre;
while (pre >= 0 && s[pre] != ' ') {
pre--;
}
int cur = pre + 1;
while (cur <= after) {
res[index++] = s[cur++];
}
res[index++] = ' ';
}
}
if (index > 0 && res[index - 1] == ' ') {
/* 输入数组中有单词的情况 */
res[index - 1] = '\0';
} else {
/* 输入数组没有单词的情况 */
res[index] = '\0';
}
return res;
}
leetcode 206.反转链表
leetcode 206.反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
使用双指针将当前头结点变为尾节点,尾节点变为头结点即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur = head;
struct ListNode* pre = NULL;
while (cur) {
struct ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
leetcode 19. 删除链表的倒数第 N 个结点
leetcode 19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
- 示例1: 输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
- 示例 2:输入:head = [1], n = 1 输出:[]
- 示例 3:输入:head = [1,2], n = 1 输出:[1]
思路
- 快慢指针,fast先移动 n + 1步,再同时移动fast、slow直到fast为空;
- 这样slow就移动到要删除节点的上一节点,就可以进行删除操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode));
dummyHead->val = 0;
dummyHead->next = head;
struct ListNode* fast = dummyHead;
struct ListNode* slow = dummyHead;
while (n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next;
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
struct ListNode* tmp = slow->next;
slow->next = tmp->next;
free(tmp);
return dummyHead->next;
}
leetcode 24. 两两交换链表中的节点
leetcode 24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
模拟过程如下:
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* swapPairs(struct ListNode* head) {
/* 定义虚拟头结点 */
struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode));
dummyHead->val = 0;
dummyHead->next = head;
/* 用于遍历的当前节点 */
struct ListNode* cur = dummyHead;
while(cur->next != NULL && cur->next->next != NULL) {
/* 记录当前的第一、三个节点 */
struct ListNode* tmp = cur->next;
struct ListNode* tmp1 = cur->next->next->next;
/* 进行交换 */
cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;
/* 更新当前节点cur */
cur = cur->next->next;
}
/* 返回值是虚拟头节点的next */
return dummyHead->next;
}
leetcode 142. 环形链表 II
leetcode 142. 环形链表 II
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
/* 定义快慢指针 */
struct ListNode *fast, *slow;
/* 都指向表头结点 */
fast = slow = head;
while (fast && fast->next) {
slow= slow->next;
fast = fast->next->next;
/* 因为快指针移动速度是慢指针的两倍
* 所以当进入环中,快指针一定会赶上慢指针
*/
if (fast == slow) {
break;
}
}
/* 若fast为空、fast->next为空,说明没有环 */
if (fast == NULL || fast->next == NULL) {
return NULL;
}
/* 上述if不成立,则表示有环
* 将慢指针slow移动到表头head
*/
slow = head;
/* 快慢指针同步移动,一定会在环的起点相遇 */
while (slow != fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
leetcode 15.三数之和(哈希复杂,双指针简单)
leetcode 15.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
- 给定数组 nums = [-1, 0, 1, 2, -1, -4],
- 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
此题可以使用set当做哈希表来求解,但是题目要求不能出现重复的三元组,所以哈希表求解不合适参考代码随想录的讲解
C++使用set:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[j], c = -(a + b)
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
continue;
}
if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
continue;
}
unordered_set<int> set;
for (int j = i + 1; j < nums.size(); j++) {
if (j > i + 2
&& nums[j] == nums[j-1]
&& nums[j-1] == nums[j-2]) { // 三元组元素b去重
continue;
}
int c = 0 - (nums[i] + nums[j]);
if (set.find(c) != set.end()) {
result.push_back({nums[i], nums[j], c});
set.erase(c);// 三元组元素c去重
} else {
set.insert(nums[j]);
}
}
}
return result;
}
};
此题使用双指针:
C语言代码:
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
/* qsort的比较函数 */
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
/* 先记录返回的行数为0 */
*returnSize = 0;
/* 输入为空、或元素个数小于3则返回NULL */
if (nums == NULL || numsSize < 3) {
return NULL;
}
/* 将nums排序为升序排列 */
qsort(nums, numsSize, sizeof(int), cmp);
/* 分配返回数组、返回数组的列数 */
int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
/* 排序后的数组从头到尾进行遍历 */
for (int i = 0; i < numsSize; i++) {
/* 当前数组大于0,表示后续没有三数之和为0,结束遍历 */
if (nums[i] > 0) {
break;
}
/* 当前元素与上一次相等,跳过此次计算,去重 */
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
/* 定义左右指针 */
int left = i + 1, right = numsSize - 1;
/* 开始查找三数之和为0 */
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
ret[*returnSize][0] = nums[i];
ret[*returnSize][1] = nums[left];
ret[*returnSize][2] = nums[right];
/* 返回数组当前行的列数为3 */
(*returnColumnSizes)[*returnSize] = 3;
/* 返回数组的行数自加1 */
(*returnSize)++;
/* 对左右指针进行去重 */
while (left < right && nums[left] == nums[++left]);
while (left < right && nums[right] == nums[--right]);
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return ret;
}
leetcode 18.四树之和(哈希复杂,双指针简单)
leetcode 18.四树之和(哈希复杂,双指针简单)
题意:
- 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例:
- 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
C语言代码:
- 此题也是可以使用哈希表进行求解,但是比较复杂,涉及到重复元素去重问题,所以改为双指针求解
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
if (nums == NULL || numsSize < 4) {
return NULL;
}
qsort(nums, numsSize, sizeof(int), cmp);
int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
*returnColumnSizes = (int*)malloc(numsSize * numsSize * sizeof(int));
for (int i = 0; i < numsSize; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < numsSize; j++) {
int sum1 = nums[i] + nums[j];
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int L = j + 1, R = numsSize - 1;
while (L < R) {
int sum2 = nums[L] + nums[R];
if (sum1 + sum2 == target) {
ret[*returnSize] = (int*)malloc(sizeof(int) * 4);
ret[*returnSize][0] = nums[i];
ret[*returnSize][1] = nums[j];
ret[*returnSize][2] = nums[L];
ret[*returnSize][3] = nums[R];
(*returnColumnSizes)[*returnSize] = 4;
(*returnSize)++;
while (L < R && nums[L] == nums[++L]);
while (L < R && nums[R] == nums[--R]);
} else if (sum1 + sum2 < target) {
L++;
} else {
R--;
}
}
}
}
return ret;
}