单链表逆序排序问题

Leetcode上刷到的三个单链表算法题

1.给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3
.

  //单链表结构
  public class ListNode {
      int val;
      ListNode next;
      ListNode(int x) { val = x; }
  }
  • 首先进行分析,最简单且容易想到的方法就是由一个指针temp从单链表表头head遍历到最后一个,如果temp.next= =null || temp.next.next==null,则表示已经遍历到末尾,则返回head,遍历过程中每两个交换一下位置
//长度不足两个时直接返回head
if(head==null || head.next==null){
    return head;
}
//定义三个指针,pre,temp,after,temp用于遍历,after用于记录temp.next的位置
//pre用于记录temp前面的位置
ListNode pre;
ListNode temp=head;
ListNode after=temp.next;
//第一次交换之后第一位与第二位交换,head指向第二位
head=after;
temp.next=after.next;
after.next=temp;
//判断temp后两位是否为空,若为空则不进行交换操作
while(temp.next!=null && temp.next.next!=null){
    //移位
    pre=temp;
    temp=temp.next;
    after=temp.next;
    //交换位置
    temp.next=after.next;
    after.next=temp;
    pre.next=after;
}
return head;

执行结果如下
在这里插入图片描述

  • 上述方法想起来比较简单,但代码过于冗余,若要简化代码量,则需要用到递归,具体思路为:开始判断head || head.next是否为空,若为空则不进行交换,直接返回head,判断之后则进行交换位置操作,这里我们至于要定义一个指针temp=head,head=head.next将head与temp进行交换,交换完之后则用递归进行下一轮交换,缺点
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        //temp指针
        ListNode temp=head;
        //交换
        head=head.next;
        temp.next=head.next;
        head.next=temp;
        //递归
        temp.next=swapPairs(temp.next);
        return head;
    }
}

执行结果如下,该方法优点就是代码简洁,缺点就是每次递归都会新建一个List Node temp,内存损耗稍微大一些
在这里插入图片描述2.给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5

  • 这个题是上一个题的强化,还是用递归的方法来解。首先分析题,实际上为长度为k的单链表的逆序问题,逆序操作之后在进行递归。
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        //k==1则直接返回
        if(k==1){
            return head;
        }
        //定义t指针遍历head后面k个节点,若长度不足k,则不进行操作直接返回
        ListNode t=head;
        for(int i=0;i<k;i++){
            if(t==null){
                return head;
            }
            t=t.next;
        }
        //定义三个指针,pre,temp,after
        ListNode pre=head;
        ListNode temp=head.next;
        ListNode after=temp.next;
        //逆序
        temp.next=pre;
        for(int i=0;i<k-2;i++){
            pre=temp;
            temp=after;
            after=after.next;
            //指针反向实现逆序
            temp.next=pre;
        }
        //递归
        head.next=reverseKGroup(after,k);
        //因为交换位置,temp变为头指针
        return temp;
    }
}

3.对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5

在这里插入图片描述

  • 正常插入排序,时间复杂度较大,理解起来也比较绕,定义四个指针,now,pre,temp,last。now指向当前需要排序的节点,last指向已经排好序的部分的最后一个节点,即now的前一个节点。temp用于排序链表遍历,pre指向temp的前一个节点,方便进行插入操作。
class Solution {
    public ListNode insertionSortList(ListNode head) {
        //head为空则返回
        if(head==null){
            return head;
        }
        //定义四个指针
        ListNode now=head.next;
        ListNode pre;
        ListNode temp;
        ListNode last=head;
        //now遍历
        while(now!=null){
            pre=head;
            temp=head.next;
            //如果now的值大于head的值,则进行插入排序
            if(now.val < pre.val){
                last.next=now.next;
                now.next=head;
                head=now;
                now=last.next;
            }else {
                //temp遍历进行插入排序
                while(temp!=now){
                    if(temp.val>now.val){
                        last.next=now.next;
                        pre.next=now;
                        now.next=temp;
                        break;
                    }
                    //temp后移
                    pre=temp;
                    temp=temp.next;
                }
                //遍历完之后last指针后移,不需要插入排序
                if(temp==now){
                    last=last.next;
                }
            }
            //now指针指向last.next
            now=last.next;
        }
        return head;
    }
}

在这里插入图片描述

  • 上述方法时间复杂度稍大,如果需要对时间进行优化,则可以新建一个单链表用于存储排序之后的链表,但内存稍有损耗
public ListNode insertionSortList(ListNode head) {
        if(head==null||head.next==null){
            return head;
        }
        //定义node指针指向新建节点,新建节点的数值为Integer的最小值
        ListNode node=new ListNode(Integer.MIN_VALUE);
        //node.next指向head
        node.next=head;
        //op指向待排序链表第一个节点进行遍历插入排序
        //right指向op前一个节点
        //temp指向已排序链表进行遍历然后与待插入节点right进行比较
        ListNode right=node,op,temp;
        while(right.next!=null){
            op=right.next;
            //如果op比right大,则不需要排序
            if(op.val>=right.val){
                //right移位
                right=right.next;
            }
            else{
                temp=node;
                //temp遍历找到待插入位置
                while(temp.next.val<op.val){
                    temp=temp.next;
                }
                //插入
                right.next=op.next;
                op.next=temp.next;
                temp.next=op;
            }
        }
        return node.next;
    }

执行结果如下,明显时间缩短
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值