奇偶链表
-
题目介绍
-
思路分析
- 顺着题目的意思去分析思路:首先题目要求将链表中的奇偶节点分别排在一起,因此我们首先将奇偶节点分别排在一起,然后再将两个断开的链表来连接起来
- 因此题目本身并不难,主要是细节的处理,比如说在遍历一遍链表的前提下,如何就将奇偶节点分开;针对奇数或偶数个节点是否有什么不一样的处理方式等
- 首先设置奇偶链表的头节点分别指向头节点和头结点的next,由于到最后我们还要将两个断开的链表分开,因此我们需要保证奇链表的尾节点和偶链表的头节点要保证,因此要额外设立一个节点来指向偶链表的头节点。此外我们直到奇链表是在偶链表之前的,当链表数为奇数个时,偶数链表的最后一个节点的next域不为null,因此我们还需要将偶数链表的尾巴节点设置为null
- 图解:
- 相关代码片段
public class Solution {
public ListNode oddEvenList(ListNode head) {
保证链表不为null
if(head == null){
return null;
}
//奇数链表的遍历节点,最终指向尾巴
ListNode odd = head;
//偶数链表遍历节点,最终指向尾巴
ListNode oven = head.next;
//偶数链表的头节点,便于连接
ListNode tmp = head.next;
//链表中只有1个元素或2个元素
if(oven == null||oven.next == null){
return head;
}
//开始遍历链表,奇偶遍历节点每次移动2步即可
while(odd.next.next!= null||oven.next.next!= null){
if(odd.next.next!= null){
odd.next = odd.next.next;
odd = odd.next;
}
if(oven.next.next!= null){
oven.next = oven.next.next;
oven = oven.next;
}
//当奇偶中出现一个链表遍历完就说明到达了原链表的尾部
if(oven.next == null||odd.next == null){
break;
}
}
//连接两个"断开"的链表
odd.next = tmp;
oven.next = null;
return head;
}
}
分隔链表
-
题目介绍
-
思路分析
- 首先,根据题意,我们可以先大致将链表长度len与k分为这么三种情况:k>len,说明原链表被分成了len个单节点加(k-len)个null节点;k==len,说明原链表被分成了len(k)个单节点;当k<len,说明链表被分成了k部分,分开的每个链表中的节点至少为1个
- 其次,我们将k<len时的情况进行细分:当len可以整除k时,说明被分隔的k个链表每个链表的长度可以保证正好相同;当len无法整除k时,根据题目要求分隔的链表中排在前面的部分的长度要大于等于后面部分,因此前面len%k个链表的长度要比后面部分的链表的长度大1
- 图解:
- 相关代码片段
public class Solution {
public ListNode[] splitListToParts(ListNode head, int k) {
int length = 0;
int a = 0;
定义存储分开链表的链表数组
ListNode[] lists = new ListNode[k];
保证链表不为null
if(head == null){
return lists;
}
int len = 0;
ListNode cur = head;
//获取链表长度
while(cur != null){
len++;
cur = cur.next;
}
cur = head;
int num = len/k;
int r = len%k;
//第一种情况len<k和第二种情况len==k
if(num == 0){
while(cur != null){
ListNode curNext = cur.next;
cur.next = null;
lists[a++] = cur;
cur = curNext;
}
}else{
ListNode tmp = head;
ListNode newHead = head;
ListNode tmp1 = head;
//第三种情况 len>k且len不能整除k
if(r!=0){
while(r>0){
for(int i = 1; i < num + 1; i++){
tmp = tmp.next;
}
tmp1 = tmp.next;
tmp.next = null;
lists[a++] = newHead;
newHead = tmp1;
tmp = tmp1;
r--;
}
while(tmp != null){
for(int i = 1; i < num; i++){
tmp = tmp.next;
}
tmp1 = tmp.next;
tmp.next = null;
lists[a++] = newHead;
newHead = tmp1;
tmp = tmp1;
}
//第三种情况len>k且len能整除k
}else{
while(tmp != null){
for(int i = 1; i < num; i++){
tmp = tmp.next;
}
tmp1 = tmp.next;
tmp.next = null;
lists[a++] = newHead;
newHead = tmp1;
tmp = tmp1;
}
}
}
return lists;
}
}
交换链表节点
-
题目介绍
-
思路分析
- 看到题目中的"交换值"的时候,由衷感慨道终于不用改变指向了。所以这道题主要就在于取得正数第k个节点和倒数第k个节点
- 正数第k个节点很好遍历,因为单链表就是往后指的,只要从头走k-1步就行了
- 由于单链表的单一指向问题,在获得倒数第k个节点我们无法从尾部向前遍历。由于获得单链表倒数第k个节点的问题在之前的这篇文章链接: 单链表操作详解(反转、遍历).中详细讲过,因此这里不再作详细解释原理,获取倒数第k个节点只需要设置两个指针fast、slow初始指向头节点,fast先走k-1步,然后fast和slow同时1步走,当fast走到链表末尾,slow指向的节点即为倒数第k个节点
- 然后交换两个节点的值即可(交换两个数不会有人不会把…)
- 相关代码片段
public class Solution {
public ListNode swapNodes(ListNode head, int k) {
ListNode slow = head;
ListNode fast = head;
for (int i = 0; i < k - 1; i++) {
fast = fast.next;
}
ListNode cur = fast;
while(fast.next!=null){
slow = slow.next;
fast = fast.next;
}
int temp = cur.val;
cur.val = slow.val;
slow.val = temp;
return head;
}
}
排序链表
-
题目介绍
-
思路分析
- 由于题目中要求要用nlog(n)的时间复杂度,因此对于冒泡排序法、选择排序法这些简单的算法无法用。在这里我们介绍一种归并排序的算法
- 归并排序:将一组数分成长度尽可能相同的两部分.分别对这两部分进行排序然后将这两部分连起来;对分开的两部分继续分割成尽可能相同的两部分,一直"套娃"直到每组数只有1个或0个,然后每次排完需以后将两部分连起来。
- 根据归并排序和单链表的特点,我们需要这么几个步骤:1. 我们需要找到一个单链表的中间节点。关于如何得到单链表的中间节点,可以参考我之前的这篇文章:单链表操作详解(反转、遍历)2. 然后递归直到每个部分的节点数为1或0。3. 在分隔两部分后,我们还要合并两个有序的链表。关于如何合并两个有序的链表,可以参考我之前的这篇文章:单链表操作详解(反转、遍历)。
- 图解:
- 相关代码片段
public class Solution1 {
public ListNode sortList(ListNode head){
return sortList(head,null);
}
public ListNode sortList(ListNode head,ListNode tail){
if(head == null){
return null;
}
if(head.next == tail){
head.next = null;
return head;
}
ListNode slow = head,fast = slow;
//寻找链表的中间节点
while(fast != tail){
slow = slow.next;
fast = fast.next;
if(fast != tail){
fast = fast.next;
}
}
ListNode mid = slow;
//递归分别对两个部分链表排序
ListNode list1 = sortList(head,mid);
ListNode list2 = sortList(mid,tail);
return merge(list1,list2);
}
//合并两个有序链表
public ListNode merge(ListNode head1,ListNode head2){
ListNode newHead = new ListNode(-1);
ListNode cur = newHead;
ListNode cur1 = head1,cur2 = head2;
while(cur1!=null&&cur2!=null){
if(cur1.val<cur2.val){
cur.next = cur1;
cur = cur1;
cur1 = cur1.next;
}else{
cur.next = cur2;
cur = cur2;
cur2 = cur2.next;
}
}
if(cur1 != null){
cur.next = cur1;
}else if(cur2 != null){
cur.next = cur2;
}
return newHead.next;
}
}