双指针解决数组链表问题

使用双指针解决数组问题

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

 

示例

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。
 

提示:

0 <= nums.length <= 50000
1 <= nums[i] <= 10000


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof
 

解题思路

一、双指针,取余

1. 使用双指针分别指向数组首位元素下标;

2. 当左下标小于右下标,且当前左下标对应元素为奇数,左指针就一直往后移动,直达指向偶数;

3. 当左下标小于右下标,且当前右下标对应元素为偶数,右指针就一直往前移动,直到指向奇数;

4. 当左指针指向偶数,右指针指向奇数时,两个元素交换位置;

5. left = right时,遍历结束,返回数组即为所求结果。

代码实现:

func exchange(nums []int) []int {
    left, right := 0, len(nums) - 1
    for left < right {
        for (left < right) && (nums[left] % 2 == 1) {   // 左边指针遇到奇数一直后移,直到遇到偶数
            left++
        }
        for (left < right) && (nums[right] % 2 == 0) {  // 右边指针遇到偶数一直前移,直到遇到奇数
            right--
        }
        nums[left], nums[right] = nums[right], nums[left] // 直到指针指向偶数,右指针指向奇数,此时交换
    }
    return nums
}

运行效果:

二、双指针,按位与

代码实现:

func exchange(nums []int) []int {
    left, right := 0, len(nums) - 1
    for left < right {                  // 左下标小于由下表就一直循环,直到相等结束循环
        if(nums[left] & 1 == 1) {       // 左下标对应元素为奇数,任何奇数与1按位与结果为1
            left++                      // 左指针向后移动
            continue                    // 跳过后面的程序,进入下一轮循环
        }
        if(nums[right] & 1 == 0) {      // 由下标对应元素为偶数,任何偶数与1按位与结果为0
            right--                     // 右指针向前移动
            continue                    // 跳过后面的程序,进入下一轮循环
        }
        nums[left], nums[right] = nums[right], nums[left]  // 直到左右指针分别指向奇数和偶数,交换
    }

    return nums
}

运行效果:

 

使用双指针解决链表问题

 

剑指 Offer 22. 链表中倒数第k个节点

问题描述

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof
 

解题思路

 

一、左右双指针:

1. 声明left,right两个指针为ListNode的指针类型(这样不用判断k是否大于链表长度,直接返回遍历结束时left指向的链表即为结果);

2. 当前指针先指向链表首节点,然后这个遍历链表;

3. 使用num统计当前遍历的节点个数,如果等于k,则左指针指向链表首节点,右指针指向当前正在遍历的节点;

4. 当走过的节点数num大于k时,左右指针一起往后遍历,直达链表遍历结束;

5. 链表遍历结束,不管链表长度是否大于k,直接返回left指向的节点即可(因为,如果链表长度小于k,left指向空节点;大于等于k则不为空)。

实现代码:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */

func getKthFromEnd(head *ListNode, k int) *ListNode {
    var left, right *ListNode  // 前后双指针
    cur := head                // cur表示当前节点,先指向首节点
    var num int                // 统计走过的节点个数
    for cur != nil {           // 链表逐节点遍历
        num++                  // 统计当前遍历的节点数
        cur = cur.Next         // 统计完节点数,当前头节点指向下一个节点
        if num == k {          // 如果遍历的节点数等于k,则左右指针分别指向链表首节点和当前节点
            left = head        // 左指针指向首节点
            right = cur        // 右指针指向当前节点
            
        } else if num > k {    // 然后,左右指针一起往后移动,直到当前节点指向链表尾部nil节点
            left = left.Next
            right = right.Next
        }
    }

    return left                 // 遍历结束,返回左指针即为所求
}

运行结果:

二、快慢指针

主要思路:

1. 当快指针 fast 走了k步的时候慢指针slow 开始走 

2.当慢指针 slow 走出的时候(即超出边界)此时的 slow 位置即为链表中倒数第k个节点 

 

代码实现:

func getKthFromEnd(head *ListNode, k int) *ListNode {
    slow, fast, cur := head, head, head  // 快慢、当前指针分别指向头节点
    for cur != nil {                     // 当前指针沿着链表逐节点遍历
        fast = fast.Next                 // 快指针先走k步
        if k > 0 {                       // 统计什么时候走过k个节点
            k--
        } else {                         // 当走过k个节点后,慢指针才跟快指针一起往后遍历
            slow = slow.Next
        }
        cur = cur.Next                   // 当前节点后移
    }

    return slow                          // 返回慢指针
}

运行效果:

 

61. 旋转链表

问题描述

难度中等504收藏分享切换为英文接收动态反馈

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例说明

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

输入:head = [0,1,2], k = 4
输出:[2,0,1]

 

提示:

  • 链表中节点的数目在范围 [0, 500] 内
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

思路分析

1. 定义慢指针slow和快指针fast,其初始都指向链表头节点。

2. 让快指针fast先向前移动k步。

3. 慢指针slow和快指针fast同时向前移动,每次移动一步,直到快指针fast指向链表的尾节点。这里,快指针fast指向链表的尾节点,不再继续向后移动的原因是,需要将尾节点和链表头节点相连,因此其所指节点不能为null。此时,慢指针slow所指节点的下一个节点就是倒数第K个节点。

4. 将快指针fast所指的尾节点的后继指针指向链表头节点,使链表成环。

5. 倒数第K个节点作为链表旋转后的新的头节点,指针slow所指节点作为新的尾节点。

代码实现

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func rotateRight(head *ListNode, k int) *ListNode {
    if head == nil {            // 链表为空,count=0,直接返回
        return nil 
    }
    var count int 
    cur := head 
    for cur != nil {            // 计算链表长度
        count++
        cur = cur.Next
    }

    k = k % count               // 当做环形链表的循环移动,当k超过链表长度时,人为移动k与k%count个节点效果一样
    slow, fast := head, head    // 快慢指针,初始时都指向头结点
    for i := 0; i < k; i++ {    // 快指针先移动k个节点
        fast = fast.Next    
    }
    for fast.Next != nil {      // 快指针移动k个节点后,快慢指针一起向后移动,直到快指针移动到最后一个节点,此时fast.Next = nil
        fast = fast.Next
        slow = slow.Next
    }

    fast.Next = head            // 链表的尾节点指向原来的头结点
    cur = slow.Next             // slow节点的Next为旋转后新链表的头结点
    slow.Next = nil             // slow节点为新链表的尾节点,断开之后的连接

    return cur 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

love666666shen

谢谢您的鼓励!

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

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

打赏作者

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

抵扣说明:

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

余额充值