day20-双指针-977/189/167/344/557/876/19-(全是双指针写法)

153 阅读4分钟

第一题-有序数组的平方

题目

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序

image.png

思路

因为刷题嘛,这个归到双指针的类别下,我就全部用的双指针,(用左右指针解释)

1、找到正负相交的那个节点为左指针,左指针加一为右指针,对比左右两边值的平方,小的放进结果里

  • right为数组长度的时候,右指针已经遍历到末尾,但是左指针没有遍历结束,直接遍历左指针,把值塞到结果里
  • 同理,当左指针小于0的时候,说明左边的已经遍历完毕,直接遍历右指针,塞到结果里。
  • 剩下的情况就是对比左右两边的平方,哪边的小,先塞进结果了,然后继续遍历小的那边。

2、当然,这个也可以直接乘方,然后sort

代码

class Solution {
    public int[] sortedSquares(int[] nums) {
        int left = 0;
        int n = nums.length;
        for(int i = 0; i < n; i++) {
            if(nums[i] < 0) {
                left = i;
            }else {
                break;
            }
        };
        int[] res = new int[n];
        int right = left + 1;
        int index = 0;
        while(left >= 0 || right < n) {
            if(right == n) {
                int leftRes = nums[left] * nums[left];
                res[index] = leftRes;
                index++;
                left--;
                continue;
            }
            if(left < 0) {
                int rightRes = nums[right] * nums[right];
                res[index] = rightRes;
                index++;
                right++;
                continue;
            }
            int leftRes = nums[left] * nums[left];
            int rightRes = nums[right] * nums[right];
            if(leftRes > rightRes) {
                res[index] = rightRes;
                index++;
                right++;
                continue;
            }else {
                res[index] = leftRes;
                index++;
                left--;
                continue;
            }
        }
        return res;
    }
}

第二题-轮转数组

题目

给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数

image.png

思路

1、遍历,整个数组遍历k%n遍,n是数组长度,可以说是暴力破解了

2、数组反转,三次反转就可以得到(可以理解成双指针)

代码

1、遍历

var rotate = function(nums, k) {
    let n = nums.length - 1;
    for(let i = 0; i < k % (n+1); i++) {
        const last = nums[n];
        for(let j = n; j >= 0; j--) {
            if(j == 0) {
                nums[j] = last;
            }else {
                nums[j] = nums[j -1 ];
            }
        }
    }
    return nums;
};

2、反转数组

/**
* start就是第一个指针,end是第二个指针
**/
const reverse = (nums, start, end) => {
    while (start < end) {
        const temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
        start += 1;
        end -= 1;
    }
}

var rotate = function(nums, k) {
    k %= nums.length;
    reverse(nums, 0, nums.length - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, nums.length - 1);
};

第三题-两数之和2-输入有序数组

题目

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 ****非递减顺序排列 ** ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 **和 **index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间

image.png

思路

双指针,left为最左边的元素,right为最右边的元素,两个元素相加,要是大于目标元素,右指针左移,要是小于目标元素,左指针右移

代码

var twoSum = function(numbers, target) {
    let left = 0;
    let right = numbers.length -1;
    while(left < right) {
        if(numbers[left] + numbers[right] > target) {
            right--;
        }else if(numbers[left] + numbers[right] < target) {
            left++;
        }else {
            break;
        };
    };
    return [left+1, right+1];

};

第四题-反转字符串

题目

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组**、使用 O(1) 的额外空间解决这一问题

image.png

思路

和第二题的反转数组一个性质,两个指针分别在两边,然后互换元素值,继续遍历

代码

var reverseString = function(s) {
    let left = 0;
    let right = s.length - 1;
    while(left < right) {
        const flag = s[left];
        s[left] = s[right];
        s[right] = flag;
        left++;
        right--;
    }
    return s;
};

第五题-反转字符串中的单词

题目

给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序

image.png

思路

(悄悄说一句,使用js写算法,动不动就超时,动不动就超时,不知道是我的问题,还是js拉垮,真讨厌啊) 1、分解字符串为数组(空格为间隔)

2、遍历分解字符串后形成的数组

3、数组中每个元素再次进行分解(因为字符串是不可变的,只能分解成数组,操作替换,再合成字符串)

4、第三步中的操作替换就是第二题的反转数组思路

代码

var reverseWords = function(s) {
    let sArr = s.split(" ");
    for(let i = 0; i < sArr.length; i++) {
        let left = 0;
        let sArrIArr = sArr[i].split('');
        let right = sArrIArr.length - 1;
        while(left < right) {
            const flag = sArrIArr[left];
            sArrIArr[left] = sArrIArr[right];
            sArrIArr[right] = flag;
            left++;
            right--;
        };
        sArr[i] = sArrIArr.join('');
    };
    return sArr.join(" ");
};

第六题链表的中间节点

题目

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点

思路

双指针之快慢指针,快慢指针,链表题经常用到的算法

1、low为慢指针,每次走一步,fast为快指针,每次走两步,当fast走到最后时候,low必然走在中间

代码

var middleNode = function(head) {
    let low = head;
    let fast = head;
    while(fast != null) {
        if(fast.next != null) {
            low = low.next;
            fast = fast.next.next;
        }else {
            break;
        }
    }
    return low;
};

第七题-删除链表的倒数第n个节点

题目

给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点

image.png

思路

1、借助栈-弹出栈的第 n 个节点就是需要删除的节点

2、双指针-直接上代码吧,我看官方文字描述好多,脑袋疼

  • 先让第一个指针走,走n步
  • 第一个指针走完n步后,两个指针一起走,一直走到第一个指针已经到最后的时候,第二个指针的next就是要删除的节点(因为第一个指针比第二个指针快n步啊)

代码

1、栈

var removeNthFromEnd = function(head, n) {
    const stack = [];
    let headCopy = new ListNode(0, head);
    let cur = headCopy;
    while(cur != null) {
        stack.push(cur);
        cur = cur.next;
    }
    for(let i = 0; i < n; i++) {
        stack.pop();
    };
    const res = stack.pop();
    res.next = res.next.next;
    stack.push(res)
    let resLast = headCopy.next;
    return resLast;
};

2、双指针

var removeNthFromEnd = function(head, n) {
    let res = new ListNode(0, head);
    let first = head;
    let second = res;
    for(let i = 0; i < n; i++) {
        first = first.next;
    }
    while(first != null) {
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return res.next;
};