A-调整数组顺序&链表中倒数第k个结点

题目14:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶说位于数组的后半部分。
分析:
  最简单的思路是从头到尾扫描这个数组,每碰到一个偶数,就拿出这个偶数,并把后面所有的数字向前移动一位,最后把这个偶数放至数组末尾。由于每碰到一个偶数就需要移动O(n)个数字,因此总的时间复杂度是O(n^2)。
  另一种做法是:发现偶数在奇数的前面,则交换他们的位置。可维护两个指针,第一个指针指向数组的第一个元素,它只向后移动;第二个指针指向数组的最后一个元素,它只向前移动。在两个指针相遇指针之前,第一个指针总是位于第二个指针之前,如果第一个指针指向的数字是偶数,且第二个指针指向的数字是奇数,就交换两个数字的位置。过程如下所示:
在这里插入图片描述
在这里插入图片描述
  考虑使用位运算,奇数&1必然=1,偶数&1必然等于0,以此区分是奇数还是偶数。代码如下所示:

    public static void reorderAddEven(int[] array) {
        if (array == null || array.length == 0) return;
        int i = 0, j = array.length - 1, temp;
        while (i < j) {
            while (i < j && (array[i] & 1) == 1) i++;
            while (i < j && (array[j] & 1) == 0) j--;
            if (i < j) {
                temp = array[i];
                array[i] = array[j];
                array[j] = array[temp];
            }
        }
        for (int k = 0; k < array.length; k++) {
            System.out.print(array[k] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        reorderAddEven(new int[]{2, 4, 6, 1, 2, 4, 6, 7, 9, 84, 3});
        reorderAddEven(new int[]{2, 4, 6, 8, 4, 1, 3, 5, 7, 9});
        reorderAddEven(new int[]{2});
        reorderAddEven(new int[]{});
    }

题目15:输入一个链表,输出链表中倒数第k个结点,为了符合大多数人的习惯,本体从1开始计数,即链表的尾结点是倒数第1个结点。链表定义如下

    public class Node<T> {
        public T item;
        public Node<T> next;

        public Node() { }

        public Node(T item) {
            this.item = item;
        }
    }

分析:
  最直观的想法是先走到链表的尾端,再从尾端回溯k步,但是此链表是单向的,因此这种思路行不通。假设整个链表有n个结点,那么倒数第k个结点就是从头结点开始往后走n-k+1个结点。要得到链表的总结点数,需要从头开始遍历一遍链表,然后再从头走n-k+1个结点到达倒数第k个结点。需要遍历链表两遍。
  实际上只要遍历一遍就可以了。可以定义两个指针,第一个指针从链表的头结点开始向前走k-1步,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头结点开始遍历。由于两个指针的距离保持在k-1,当第一个指针达到链表的尾结点时,第二个指针正好在倒数第k个结点。过程示意图如下所示。
在这里插入图片描述
  如图所示,第一个指针从头结点在链表上向前走2(3-1)步到达第3个结点,接着让两个指针同时向后遍历,当第一个指针到达链表尾结点时,第二个指针刚好在倒数第3个结点。
  在写代码时需要注意以下几点:
  1)输入为头结点为空处理。
  2)输入的链表总结点数少于k,在头结点向前走k-1步时会造成空指针异常。因此在循环的时候可能会出现next为null的情况。
  3)输入参数k为0,即输出倒数第0个结点。由于我们设计的是从1开始的,所有0是没有意义的。
  代码如下所示:

    public static Node findKthToTail(Node pNode, int k) {
        if (pNode == null || k == 0) return null;

        Node headANode = pNode;
        Node headBNode = null;

        for (int i = 0; i < k - 1; i++) {
            if (headANode.next != null)
                headANode = headANode.next;
            else
                return null;
        }
        headBNode = pNode;
        while (headANode.next != null) {
            headANode = headANode.next;
            headBNode = headBNode.next;
        }
        return headBNode;
    }

    public static void main(String[] args) {
        Node<Integer> a = new Node<>(1);
        Node<Integer> b = new Node<>(2);
        Node<Integer> c = new Node<>(3);
        Node<Integer> d = new Node<>(4);
        Node<Integer> e = new Node<>(5);
        Node<Integer> f = new Node<>(6);

        a.next = b;
        b.next = c;
        c.next = d;
        d.next = e;
        e.next = f;

        // 头结点
//        System.out.println(findKthToTail(a,6).item);
        // 尾结点
//        System.out.println(findKthToTail(a,1).item);
        // 中间结点
//        System.out.println(findKthToTail(a,3).item);
        // null结点
//        System.out.println(findKthToTail(null,3));
        // k大于链表结点数
//        System.out.println(findKthToTail(a,7));
        // k==0
        System.out.println(findKthToTail(a, 0));
    }

变型1:求链表的中间结点。如果链表中节点数为奇数,返回中间结点;如果结束总数为偶数,返回中间两个结点的任意一个。
分析:
  可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步,当走的快的指针到达链表尾结点时,走的慢的指针正好在链表的中间。
变型2:判断一个单项链表是否形成了环形结构。
分析:
  定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步,如果走的快的指针追上了走得慢的指针,那么链表就是环形链表;如果走的快的指针到达链表的末尾都没有追上走的慢的指针,那么就不是环形链表。

tips:当使用一个指针不能解决问题时,可以尝试使用两个指针,可以让其中一个指针遍历的速度快一点,或者让它先在链表上走若干步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值