CodeTop-补充题1. 排序奇升偶降链表

题目来源

补充题1. 排序奇升偶降链表

题目详情

本文章是对企业题库CodeTop[1]的补充,汇总那些在Leetcode上找不到的面试高频题。

来看一下几篇面经的原文叙述

  • 链表,奇数位置按序增长,偶数位置按序递减,如何能实现链表从小到大?(2020.10 字节跳动-后端)[2]

  • 奇偶生序倒序链表的重新排序组合,例如:18365472(2020.08 字节跳动-后端)[3]

  • 1->4->3->2->5 给定一个链表奇数部分递增,偶数部分递减,要求在O(n)时间复杂度内将链表变成递增,5分钟左右(2020.07 字节跳动-测试开发)[4]

  • 奇数位升序偶数位降序的链表要求时间O(n)空间O(1)的排序?(2020.07 字节跳动-后端)[5]

可见,无论是后端还是测试开发,都曾被考察过这道题,而且这道题并非力扣上的题目,大家一定要注意!!


给定一个奇数位升序,偶数位降序的链表,将其重新排序。

输入: 1->8->3->6->5->4->7->2->NULL
输出: 1->2->3->4->5->6->7->8->NULL

题解分析

  1. 本题是典型的链表类型的题目,题目涉及到的操作比较多,但是都不会很难。
  2. 考虑到原始链表中存在两种类型的节点,即奇节点和偶节点,所以我们可以使用两个链表来分别存储以更好地解决问题。
  3. 将链表拆分为奇偶链表后,由于原先的偶链表是降序的,我们需要先将其翻转,使之变成升序的链表。
  4. 最后,两个链表都是有序的,那么我们很容易就可以对这两个链表进行合并以达到排序链表的作用。
  5. 本题需要注意的是,我大量地使用了【虚拟节点】,这是一个解决链表问题经常使用的一个很有用的技巧,它能保持我们随时可以拿到头节点。但是在使用虚拟节点的时候,我们需要时刻警醒,在虚拟链表的末尾需要赋值为null,否则将导致链表不正确,并在遍历时出现死循环。
package com.walegarrett.interview;

/**
 * @Author WaleGarrett
 * @Date 2022/2/6 10:06
 */

import java.util.List;

/**
 * 给定一个奇数位升序,偶数位降序的链表,将其重新排序。
 *  输入: 1->8->3->6->5->4->7->2->NULL
 *  输出: 1->2->3->4->5->6->7->8->NULL
 */



public class Sorting_Ascending_Descending_List {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }

    public static void main(String[] args) {
        int[] input = {1, 8, 3, 6, 5, 4, 7, 2};
        ListNode dumyOri = new ListNode(-1);
        ListNode ori = dumyOri;
        for(int i=0; i<input.length; i++){
            ori.next = new ListNode(input[i]);
            ori = ori.next;
        }
        ori = null;

        ListNode res = sortList(dumyOri.next);
        while(res != null){
            System.out.print(res.val + ", ");
            res = res.next;
        }
    }
    public static ListNode sortList(ListNode head){
        // 首先拆分奇偶链表
        ListNode dumyOdd = new ListNode(-1);
        ListNode odd = dumyOdd;

        ListNode dumyEven = new ListNode(-1);
        ListNode even = dumyEven;

        ListNode now = head;
        while(now != null){
            if((now.val & 1) == 1){
                odd.next = now;
                odd = odd.next;
            }else{
                even.next = now;
                even = even.next;
            }
            now = now.next;
        }
        odd.next = null;// 将尾节点置为null
        even.next = null;// 将尾节点置为null

        // 接着翻转偶数链表
        even = reverseList(dumyEven.next);
        odd = dumyOdd.next;

        // 最后合并奇偶链表
        return mergeList(odd, even);
    }

    private static ListNode reverseList(ListNode head){
        ListNode pre = null, now = head;
        while(now != null){
            ListNode temp = now.next;
            now.next = pre;
            pre = now;
            now = temp;
        }
        return pre;
    }

    private static ListNode mergeList(ListNode odd, ListNode even){
        ListNode dumyList = new ListNode(-1);
        ListNode now = dumyList;
        while(odd != null || even != null){
            if(odd == null){
                now.next = even;
                even = even.next;
            }else if(even == null){
                now.next = odd;
                odd = odd.next;
            }else{
                if(even.val <= odd.val){
                    now.next = even;
                    even = even.next;
                }else{
                    now.next = odd;
                    odd = odd.next;
                }
            }
            now = now.next;
        }
        return dumyList.next;
    }
}
上一篇:请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。题目数据保证需要删除的节点 不是末尾节点 。


下一篇:算法题目_两个有序列表的合并