5、双指针相关

上一节:4、字符串相关

下一节:6、栈与队列相关

双指针

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;
}

上一节:4、字符串相关

下一节:6、栈与队列相关

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值