链表相关的操作,主要包括删除一个节点,增加一个节点,寻找一个节点等。
以下为LeetCode上关于链表的个人解题笔记。
1.反转链表
/**
* 使用一个列表来保存所有节点的值
* 然后使用prev指向前一个节点,cur指向当前节点
* 每次插入一个的时候,将prev与cur连接起来,并更新prev
*
* @param head
* @return
*/
public ListNode reverseList(ListNode head) {
ArrayList<Integer> values = new ArrayList<>();
while(head != null){
values.add(head.val);
head = head.next;
}
ListNode first = null;
ListNode prev = null;
ListNode cur = null;
boolean isFirst = true;
for(int i=values.size()-1 ; i>=0; i--){
if(isFirst){
//第一次进来初始化first,并且让prev也指向first
isFirst = false;
first = new ListNode(values.get(i));
prev = first;
}
else{
cur = new ListNode(values.get(i));
prev.next = cur;//将前一个节点与当前节点相连
prev = cur;//更新前一个节点
}
}
return first;
}
2.删除链表中值等于某个数的所有元素。
/**
* 还是使用prev,cur指向前一个节点和当前节点
* 分要删除的节点是不是头结点两种情况
* 如果是头结点,则更新头结点,prev,cur;
* 如果不是不是头结点,则更改prev.next的指向,使其指向cur.next;
* @param head
* @param val 要删除元素的值
* @return
*/
public ListNode removeElements(ListNode head, int val) {
ListNode prev = head;
ListNode cur = head;
boolean needUpdateFirst = true;
while(cur!=null){
if(cur.val == val){
if(needUpdateFirst){//头结点的值等于val,更新头结点
head = head.next;
prev = head;
cur = head;
}
else{ //等于val的节点不是位于头结点
prev.next = cur.next;
cur = cur.next;
}
}
else{
needUpdateFirst = false;
prev = cur;
cur = cur.next;
}
}
return head;
}
3.查找两个单链表的交点开始的节点。
方法一:
主要思想是遍历两个链表保存到两个ArrayList中;
然后从后往前比较这两个ArrayList的相同项个数;
最后将这些相同项连接起来返回。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ArrayList<Integer> valuesA = new ArrayList<>();
ArrayList<Integer> valuesB = new ArrayList<>();
ListNode curA = headA;
ListNode curB = headB;
//遍历两个链表保存到两个ArrayList中
while(curA != null){
valuesA.add(curA.val);
curA = curA.next;
}
while(curB!= null){
valuesB.add(curB.val);
curB = curB.next;
}
//然后从后往前比较这两个ArrayList的相同项个数
int count = 0;
for(int i= valuesA.size()-1, j = valuesB.size()-1; i>=0 && j>=0; i--,j--){
if(!valuesA.get(i).equals(valuesB.get(j))){
break;
}
count++;
}
if(count == 0){
return null;
}
ListNode first = null, prev = null, cur = null;
boolean isFirst = true;
//将这些相同项连接起来返回。
while(count>0){
int index = valuesA.size() - count;
count --;
if(isFirst){
isFirst = false;
first = new ListNode(valuesA.get(index));
prev = first;
cur = first;
}
else{
cur = new ListNode(valuesA.get(index));
prev.next = cur;
prev = cur;
}
}
return first;
}
方法二:来自LeetCode
1.根据两个ListNode长度是否相等,有不同的执行效果。
2.当长度相等时,最多执行n次(n次时同时为null)。
例如[3,4,2,3] 和[3,4,2,4]
[3,4,1,2]和[5,6,1,2]
3.当长度不等时,最多执行m+n+2次(最后同时为空)
例如对于[3,1,4] 和[4,5,1,2]
下面的a会经过 3,1,4,null,4,5,1,2,null
下面的a会经过 4,5,1,2,null,3,1,4,null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
ListNode a = headA;
ListNode b = headB;
while(a!=b){
a = a==null? headB: a.next;
b = b==null? headA: b.next;
}
return a;
}
4.将两个单项链表相加,每个节点的值都为0-9,且没有前导0,需要逢十个进一。
第一种方法:
1.遍历将链表的值保存到ArrayList中
2.然后将短的ArrayList依次加到长的ArrayList对应位置
3.最后更新长的ArrayList的进位
4.利用长的ArrayList从前往后构建链表
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1==null || l2==null) return null;
ArrayList<Integer> firstValues = new ArrayList<>();
ArrayList<Integer> secondValues = new ArrayList<>();
ListNode first = l1;
ListNode second = l2;
while(first!=null){
firstValues.add(first.val);
first = first.next;
}
while(second != null){
secondValues.add(second.val);
second = second.next;
}
int sizeFirst = firstValues.size();
int sizeSecond = secondValues.size();
ListNode result;
if(sizeFirst>sizeSecond){
result = add(firstValues, secondValues);
}
else{
result = add(secondValues, firstValues);
}
return result;
}
//确保firstValues比secondValues数据多
private ListNode add(List<Integer> firstValues, List<Integer> secondValues){
//将第二个加到第一个上
for(int i=secondValues.size()-1, j= firstValues.size()-1;
i>=0 && j>=0; i--,j--){
firstValues.set(j, firstValues.get(j)+secondValues.get(i));
}
//更新第一个为10的节点
for(int i = firstValues.size()-1; i>=0; i--){
int value = firstValues.get(i);
if(value>=10){
firstValues.set(i, value-10);//更新当前值
if(i>=1){
firstValues.set(i-1, firstValues.get(i-1)+1);
}
else{
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.addAll(firstValues);
firstValues = arrayList;
}
}
}
//构造链表
ListNode first = null, prev = null, cur = null;
boolean isFirst = true;
for(int i=0; i<firstValues.size(); i++){
if(isFirst){
isFirst = false;
first = new ListNode(firstValues.get(i));
prev = first;
}
else{
cur = new ListNode(firstValues.get(i));
prev.next = cur;
prev = cur;
}
}
return first;
}
第二种方法:更优,将进位问题和构建链表放在一起处理了。
使用两个栈来保存遍历的值
然后利用两个栈的值从后往前构建链表
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1==null || l2==null) return null;
Stack<Integer> firstValues = new Stack<>();
Stack<Integer> secondValues = new Stack<>();
ListNode first = l1;
ListNode second = l2;
while(first!=null){
firstValues.add(first.val);
first = first.next;
}
while(second != null){
secondValues.add(second.val);
second = second.next;
}
ListNode listNode = new ListNode(0);
int sum = 0;
while( !firstValues.isEmpty() || !secondValues.isEmpty()){
if(!firstValues.isEmpty()) sum += firstValues.pop();
if(!secondValues.isEmpty()) sum+= secondValues.pop();
listNode.val = sum%10;//更新当前链表第一个节点的值
ListNode head = new ListNode(sum/10);//创建一个新的头结点
head.next = listNode; //将新的头结点与原来的链表连接
listNode = head;//更新链表
sum /= 10;
}
//确保不含有前导0
return listNode.val == 0 ? listNode.next: listNode;
}
5.给定一个单链表,将所有奇数节点组合在一起,然后是偶数节点。
请注意,我们正在谈论节点号而不是节点中的值。
解决方法:
遍历ListNode到ArrayList中
然后利用ArrayList构建链表
public ListNode oddEvenList(ListNode head) {
if(head == null){
return null;
}
ListNode myHead = head;
ArrayList<Integer> arrayList = new ArrayList<>();
while(myHead!=null){
arrayList.add(myHead.val);
myHead = myHead.next;
}
ListNode first =null;
ListNode prev = null;
ListNode cur = null;
boolean isFirst = true;
for(int i=0;i<arrayList.size();i+=2){
if(isFirst){
isFirst = false;
first = new ListNode(arrayList.get(i));
prev = first;
}
else{
cur = new ListNode(arrayList.get(i));
prev.next = cur;
prev = cur;
}
}
for(int i=1; i<arrayList.size();i+=2){
cur = new ListNode(arrayList.get(i));
prev.next = cur;
prev = cur;
}
return first;
}
6.重排单向链表
给定链表 L: L0→L1→…→Ln-1→Ln,
将该链表重排为: L0→Ln→L1→Ln-1→L2→Ln-2→…
/**
* 遍历单链表将值存到ArrayList中
* 然后使用ArrayList中的值更新原来head链表的值
* 如果为奇数个节点,则将中间的那个奇数节点(当然也包括只有一个节点的情况),更新对应的值。
* @param head
*/
public void reorderList(ListNode head) {
if(head == null){
return;
}
ListNode myHead = head;
ArrayList<Integer> arrayList = new ArrayList<>();
// 遍历单链表将值存到ArrayList中
while(myHead!=null){
arrayList.add(myHead.val);
myHead = myHead.next;
}
int left = 0, right = arrayList.size()-1;
myHead = head;
//然后使用ArrayList中的值更新原来head链表的值
while(left < right){
myHead.val = arrayList.get(left);
myHead.next.val = arrayList.get(right);
if(myHead.next != null)
myHead = myHead.next.next;
left++;
right--;
}
// 如果为奇数个节点,则将中间的那个奇数节点(当然也包括只有一个节点的情况),更新对应的值。
if(arrayList.size()%2 != 0){
myHead.val = arrayList.get(arrayList.size()/2);
}
}
7.给定一个链表,返回循环开始的节点。如果没有循环,返回null。
1.方法一:
关键:将之前遍历过的节点保存到ArrayList中
每次遇到新的节点,就判断它的下一个节点是否已经在ArrayList
如果在表明已经存在,则直接返回;
如果不存在,则将当前节点添加到ArrayList中
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
ListNode myHead = head;
ArrayList<ListNode> prevListNodes = new ArrayList<>();
while(myHead != null){
if(myHead.next != null && prevListNodes.contains(myHead.next)){
return myHead.next;
}
else{
prevListNodes.add(myHead);
myHead = myHead.next;
}
}
return null;
}
2.方法二:来自LeetCode
1.通过步长为1和步长为2的两个指针a,b寻找相遇的位置
2.利用一个新的指针c从头开始,与上述两个节点之一可以是a或者是b进行比较
找到循环开始的第一个节点。
因为找到的相遇节点的位置不同,所以需要进行步骤2
例如:
1.以首节点或尾节点开始循环,找到的相遇就是该节点
[1,2,3,4,1,2,3,4….] 以第一个节点开始循环
[1,2,3,4,4,4,4,….] 以尾节点开始循环
2.以中间节点开始循环,找到的相遇节点是循环的最后一个节点
例如,下面的找到的相遇节点就是4
[1,2,3,4,2,3,4….] 以非首尾节点开始循环,从2开始
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
ListNode a = head;
ListNode b = head;
boolean hasCycle = false;
while(a!=null && b!=null){
a = a.next;
if(b.next == null) return null;
b = b.next.next;
if(a == b){
hasCycle = true;
break;
}
}
if(!hasCycle) return null;
ListNode c = head;
while(c!=a){
c = c.next;
a = a.next;
}
return c;
}
8.复制带有随机指针的链表
class RandomListNode {
int label;
RandomListNode next, random;
RandomListNode(int x) { this.label = x; }
}
1.方法一:
/**
* 先构建一个新的链表,并将random指向的节点保存到ArrayList中
* 然后从ArrayList中依次取出random节点,并与新链表的对应节点向比较,如果相等则赋值为对应节点。
* @param head
* @return
*/
public RandomListNode copyRandomList(RandomListNode head) {
RandomListNode myHead = head;
RandomListNode copyFirst = null;
RandomListNode prev = null;
RandomListNode cur = null;
boolean isFirst = true;
ArrayList<RandomListNode> keepRandoms = new ArrayList<>();
while(myHead != null){
if(isFirst){
isFirst = false;
copyFirst = new RandomListNode(myHead.label);
prev = copyFirst;
}
else{
cur = new RandomListNode(myHead.label);
prev.next = cur;
prev = cur;
}
keepRandoms.add(myHead.random);
myHead = myHead.next;
}
myHead = copyFirst;
for(int i=0; i<keepRandoms.size(); i++){
if(keepRandoms.get(i) != null){
//如果对应的random不为空的话,则找到对应的相等节点并返回。
myHead.random = getTheEqualNode(copyFirst, keepRandoms.get(i));
}
myHead = myHead.next;
}
return copyFirst;
}
public RandomListNode getTheEqualNode(RandomListNode first, RandomListNode random){
RandomListNode randomCopy = random;
RandomListNode curFirst = first;
RandomListNode curFirstCopy = curFirst;
while(curFirst != null){
randomCopy = random;
curFirstCopy = curFirst;
boolean hasFind = true;
while(randomCopy!=null){
if(randomCopy.label!= curFirstCopy.label ){
hasFind = false;
break;
}
randomCopy = randomCopy.next;
curFirstCopy = curFirstCopy.next;
}
if(hasFind){
return curFirst;
}
curFirst = curFirst.next;
}
return null;
}
2.方法二:来自LeetCode
public RandomListNode copyRandomList(RandomListNode head) {
if (head == null) return null;
Map<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
// loop 1. copy all the nodes
RandomListNode node = head;
while (node != null) {
map.put(node, new RandomListNode(node.label));
node = node.next;
}
// loop 2. assign next and random pointers
node = head;
while (node != null) {
map.get(node).next = map.get(node.next);
map.get(node).random = map.get(node.random);
node = node.next;
}
return map.get(head);
}
9.删除有序链表的节点,使得所有值只出现一次
给定一个排序的链接列表,删除所有重复项,使每个元素只显示一次。
例如, 给定1-> 1-> 2,返回1-> 2。 给定1-> 1-> 2-> 3-> 3,返回1-> 2-> 3。
/**
* 总的来说就是不断地判断当前节点与前一个节点是否相等
* 如果相等,就删除当前节点(即将前一个节点与当前节点的下一个节点相连接),然后更新当前节点
* 如果不相等,则更新前一个节点为当前节点,然后更新当前节点
* @param head
* @return
*/
public ListNode deleteDuplicates(ListNode head) {
if(head == null){
return null;
}
ListNode prev = head;//指向前一个
ListNode cur = head.next;
while(cur != null){
//如果前一个节点等于当前节点,让前一个节点与当前节点的后一个节点连接起来(删除了当前节点)
if(prev.val == cur.val){
prev.next = cur.next;
}
//如果前一个节点不等于当前节点,则更新当前节点的
else{
prev = cur;
}
//更新当前节点
cur = cur.next;
}
return head;
}
10.删除有序链表中所有出现两次及两次以上的节点(第九题升级版)
1.方法一;利用prev,cur,curnext三个指针
/**
* 总的来说利用prev指向前一个不相同节点,利用cur指向当前节点,curnext指向下一个节点,
* 1.如果 cur.val == curnext.val,那么就不断更新curnext直到不等于cur;
* 将cur更新为curnext
* 如果是需要更新head节点的话,
* 就更新head节点
* 如果不需要的话
* 然后将prev.next与curnext指向的节点相连接
*
* 2.如果cur.val != curnext.val,那么就设置needUpdateFirst为false,
* 然后更新prev为cur,并且将cur更新为curnext
* @param head
* @return
*/
public ListNode deleteDuplicates(ListNode head) {
if(head == null) return null;
ListNode prev = null;
ListNode cur = head;
ListNode curnext = null;
boolean needupdateFirst = true;
while(cur != null){
curnext = cur.next;
if(curnext != null && cur.val == curnext.val){
while(curnext != null && cur.val == curnext.val){
curnext = curnext.next;
}
cur = curnext;
if(needupdateFirst){
head = cur;
}
else{
prev.next = cur;
}
}
else{
needupdateFirst = false;
prev = cur;
cur = curnext;
}
}
return head;
}
方法二:利用一个ArrayList A来保存所有遍历链表的值,
然后将ArrayList A中只出现一次的值保存到ArrayList B中
将利用ArrayList B构建一个链表即可。
11.旋转队列
给定列表,将列表向右旋转k个位置,其中k是非负数。
例如: 给定1-> 2-> 3-> 4-> 5-> NULL和k = 2, 返回4-> 5-> 1-> 2-> 3->空。
/**
* 解题思路:
* 1.首先遍历统计链表的节点个数,计为length
* 2.然后将k对length求余
* 3.再次遍历链表,每遍历一个节点cur,length减一,记录下一个节点next
* 如果此时length == k 表明已经找到新链表的头结点,将cur.next改为null,将next节点赋给myFirst
* 如果此时length == 0;将旧链表的最后一个节点与原来的头结点相连
* 其他情况,继续遍历下一个节点
* @param head
* @param k
* @return
*/
public ListNode rotateRight(ListNode head, int k) {
if(head ==null || k <= 0) return head;
ListNode cur = head;
int length = 0;
while(cur != null){
length ++;
cur = cur.next;
}
k = k % length;
if(k ==0) return head;
ListNode myFirst = null;
ListNode next = null;
cur = head;
while(cur != null){
length--;
next = cur.next;
if(length == k){//此时找到新的头结点
cur.next = null;
myFirst = next;
cur = myFirst;
}
else if(length == 0){//将旧链表的最后一个节点与头结点相连
cur.next = head;
break;
}
else cur = cur.next;
}
return myFirst;
}
12.合并有序列表,使得合并后的列表仍然有序
1.方法一:合并l2到l1上
/**
* 核心思想:将l2合并到l1上,l2插入到l1有三个位置队首,队中,队尾
*l1中使用两个指针prev1,cur1
*l2中使用一个指针cur2
* 内层循环
* 将prev1指向链表一中小于等于cur2的最后一个节点,
* cur1指向大于cur2或为null的节点
*如果cur1为空,将cur2插入到cur1队尾
*如果cur1不为空且等于l1,将cur2插入队首
*否则将cur2插入队列L1中
* @param l1
* @param l2
* @return
*/
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
ListNode cur1 = l1, prev1 = null;
ListNode cur2 = l2;
while(cur1 != null && cur2 != null){
while(cur1 != null && cur2!=null && cur1.val <= cur2.val){
prev1 = cur1;//不断更新prev1使得prev1指向满足条件的最后一个节点
cur1 = cur1.next;
}
if(cur1 == null){//将cur2插入到cur1队尾
prev1.next = cur2;
break;
}
else if(cur1 == l1){//将cur2插入队首
ListNode myFirstNode = new ListNode(cur2.val);
myFirstNode.next = l1;
l1 = myFirstNode;
prev1 = myFirstNode;
}
else{//将cur2插入队列L1中
ListNode insert = new ListNode(cur2.val);
ListNode oldNext = prev1.next;
prev1.next = insert;
insert.next = oldNext;
prev1 = insert;
}
cur2 = cur2.next;
}
return l1;
}
方法二:你可以遍历两个列表并将其放在同一个ArrayList中,然后
对ArrayList进行排序,最后构造一个新的链表。效率低,但是思想简单。
方法三:递归来自LeetCode
public ListNode mergeTwoLists(ListNode l1, ListNode l2){
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else{
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
13.一对一对的交换链表中的节点
给定一个链表,交换每两个相邻的节点并返回其头。
例如, 给定1-> 2-> 3-> 4,您应该以2-> 1-> 4-> 3的形式返回列表。
要求:
您的算法应该只使用恒定空间。
您不能修改列表中的值,只能改变节点本身。
/**
* 核心:更新交换奇数偶数节点,并使用prev记录之前的奇数节点
* odd指向链表中的奇数节点
* even指向链表中的偶数节点
* prev更新后的奇数节点
* @param head
* @return
*/
public ListNode swapPairs(ListNode head) {
if(head == null) return null;
ListNode odd = head;
ListNode even = odd.next;
boolean first = true;
ListNode prev = null;
while(odd != null && even != null){
odd.next = even.next;//交换奇偶数节点
even.next = odd;
if(first){//如果为第一次的话,要更改头结点
first = false;
head = even;
}
else{//如果不是第一次的话,要更新prev的指向
prev.next = even;
}
prev = odd;//使得prev指向更新后的奇数节点
odd = odd.next;
if(odd!=null){
even = odd.next;
}
}
return head;
}
14.分区链表
给定一个链表和一个值x,对其进行分区,使得小于x的所有节点都在大于或等于x的节点之前。
您应该保留两个分区中每个分区中节点的原始相对顺序。
例如, 给定1-> 4-> 3-> 2-> 5-> 2和x = 3, 返回1-> 2-> 2-> 4-> 3-> 5。
解决方法:cur用来遍历链表中的每一个节点,prev用来记录cur的前一个节点
prevLittle用来指向前一个小于x的节点。
/**
* cur用来遍历链表中的每一个节点,prev用来记录cur的前一个节点
* prevLittle用来指向前一个小于x的节点。
*
*
* 先不考虑左边小于x的节点
* 如果存在的话,更新prevLittle
* 然后在大于等于x的节点之后,寻找小于x的第一个节点
* 如果找不到的话,直接返回
* 如果存在的话,删除该中该节点
* 此时判断prevLittle是否为空
* 如果为空,则将节点插入到原头结点之前,并更新头结点,最后更新prevLittle
* 如果不为空,则将该节点插入到prevLittle之后,最后更新prevLittle
* 继续从链表中寻找下一个小于x的节点,如果找到的话,将其先删除,然后插入到prevLittle之后,并更新prevLittle
* @param head
* @param x
* @return
*/
public ListNode partition(ListNode head, int x) {
if(head == null) return head;
ListNode cur = head;
ListNode prevLittle = null;
ListNode prev = head;
while(cur!=null && cur.val <x){//不考虑左边小于x的节点
prevLittle = cur;
cur = cur.next;
}
while(cur!= null && cur.val>=x){//找到在大于等于x的节点之后,小于x的第一个节点
prev = cur;
cur = cur.next;
}
if(cur == null) return head;//找不到就满足要求,直接返回
prev.next = cur.next;//删找到的第一个中间节点
if(prevLittle == null){//更新头结点
ListNode newHead = new ListNode(cur.val);
newHead.next = head;
head = newHead;
prevLittle = head;
}
else{
ListNode node = new ListNode(cur.val);
node.next = prevLittle.next;//将cur.val插入到prevLittle之后
prevLittle.next = node;
prevLittle = prevLittle.next;
}
cur = cur.next;
while(cur != null){
while(cur!= null && cur.val >=x){//继续从链表中寻找下一个小于x的节点,
prev = cur;
cur = cur.next;
}
if(cur != null){//如果找到的话,将其先删除,
prev.next = cur.next;//删除下一个找到的节点
ListNode insert = new ListNode(cur.val);//然后插入到prevLittle之后,
insert.next = prevLittle.next;
prevLittle.next = insert;
prevLittle = insert;//更新prevLittle
cur = cur.next;
}
}
return head;
}
15.k个节点为一组进行反转
给定一个链表,一次反转链表k的节点并返回其修改列表。
k是正整数,小于或等于链表的长度。如果节点的数量不是k的倍数,
则最后的剩余节点应保持原样。
要求:您不能更改节点中的值,只有节点本身可能会更改。
只允许恒定的内存。
例如, 给出这个链表:1-> 2-> 3-> 4-> 5 对于k = 2,
您应该返回:2-> 1-> 4-> 3-> 5 对于k = 3,您应该返回:3-> 2-> 1-> 4-> 5
/**
* 先获取链表的长度length
* 然后让 extra = length % k
* 使用一个ListNode nodes[k]数组来保存当前组的节点,
* 使用prev来保存前一组的最后一个节点,用来连接不同的组
*
* 先nodes中更改节点的指向
* 如果是第一组,,需要更新头结点,
* 如果不是第一组,更改prev.next = nodes[k-1](将前一组的最后一个与当前组的第一个连接起来)
* 最后将prev赋值为nodes[0]
* @param head
* @param k
* @return
*/
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null) return null;
int length = 0;
ListNode cur = head;
while(cur != null){
length++;
cur = cur.next;
}
if(k > length) return head;
int extra = length % k;
ListNode nodes[] = new ListNode[k];
ListNode prev = null;
cur = head;
int index = 0;
while(cur != null){
if(length == extra) break;//多余部分不需要处理
nodes[index] = cur;
cur = cur.next;
index++;
if(index == k){//已经到一组的末尾了
index = 0;
for(int i=0; i<k; i++){//更新节点的指向
System.out.println(nodes[i].val);
if(i == 0) nodes[0].next = nodes[k-1].next;
else nodes[i].next = nodes[i-1];
}
if(prev == null) head = nodes[k-1];///表明是第一组,更新头结点的指向
else prev.next = nodes[k-1]; //不是第一组,将前一组的最后一个与当前组的第一个连接起来
prev = nodes[0];//更新prev指向当前组的最后一个节点
}
length --;
}
return head;
}