1.指定区间反转
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
我的思考:如果链表只有一个结点直接返回。
如果不是,遍历链表的同时,确定指针位置。反转leftNode为头结点,rightNode为尾结点的链表,再修改指针指向。
第二种方法,用链表循环的方式,将为区间尾结点插入到头结点之前。问题:如果left right 触碰到了区间边界怎么处理。
头插法:也就是第二种方法。区间尾结点插到头结点上。
如果使用链表循环的方式,首先要确定位置,left为1的时候情况分开来,然后保存第三部分头结点right.next,没有就不保存,这个时候就可以不管第三部分头结点了。然后直接对链表进行链表循环,倒数第right - left +1 个结点开始循环。但是有一个明显的问题,这段代码不注意就会出现环,因为我们没有把循环部分从链表中断开,而且修改指针会很麻烦。所以不能用链表循环的思路来做。
其实在第一次遍历到反转区间的时候,我们可以把反转区间这个大问题分解成为:将当前right指针指向的结点,插到已经反转完成的区域之前。
那么这个函数的开始部分是right指向left.next的时候。结束部分是,right移动次数 == right-1;
1.1头插法(重要)
理论成立,开始尝试。
代码:
lass Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head == null || head.next==null || right==left)
return head;
int left2=left;
ListNode dummyNode = new ListNode();
dummyNode.next = head;
ListNode leftNode = head;
while(left2-->1)
{
dummyNode=dummyNode.next;
}
leftNode = dummyNode.next;
ListNode curNode = leftNode.next;
ListNode temp ;
for(int i=0;i<right-left;i++)
{
if(i==0 )
{
leftNode.next = null;
}
if(curNode==null || curNode.next==null)
return head;
temp = curNode.next;
curNode.next = leftNode;
dummyNode.next= curNode;
leftNode = curNode;
curNode = temp;
}
while(head.next!=null)
{
head=head.next;
}
head.next=curNode;
return dummyNode;
}
}
这段代码问题很大。重写
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 设置 dummyNode 是这一类问题的一般做法
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
ListNode cur = pre.next;
ListNode next;
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummyNode.next;
}
}
1.2穿针引线法
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
// 建议写在 for 循环里,语义清晰
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
ListNode rightNode = pre;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
// 第 3 步:切断出一个子链表(截取链表)
ListNode leftNode = pre.next;
ListNode curr = rightNode.next;
// 注意:切断链接
pre.next = null;
rightNode.next = null;
// 第 4 步:同第 206 题,反转链表的子区间
reverseLinkedList(leftNode);
// 第 5 步:接回到原来的链表中
pre.next = rightNode;
leftNode.next = curr;
return dummyNode.next;
}
private void reverseLinkedList(ListNode head) {
// 也可以使用递归反转一个链表
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
}
}
2.两两交换链表中的结点(重要)
其实只要把指定区间反转中的 头插法 理解透彻就能轻松举一反三。、
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
我的思考:因为肯定要处理头结点,所以加一个虚拟头结点用来方便操作。然后每一组交换的前一个结点都能找到,0 2 4 6……。然后每一次交换用两个局部变量存储两个结点,就能轻松反转两个结点。
代码:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
while(pre.next!=null && pre.next.next!=null)
{
ListNode node1 = pre.next;
ListNode node2 = pre.next.next;
node1.next = node2.next;
node2.next = node1;
pre.next = node2;
pre = pre.next.next;
}
return dummyHead.next;
}
}
3.单链表加1(作业+栈的解决办法)
用一个非空单链表,表示一个非负整数,将这个整数加一。
我的思考:首先,这条题关键在于进位。有两种情况,0和其他数。
如果当前结点进位了,就存下这个,保存到虚拟节点中。如果上一个结点有数字就加上去,没有就把虚拟节点接在链表上。
又进位了就重复这个过程,直到虚拟节点+高位结点<10。
问题关键,高位在表头,所以我们先反转链表,再反转回来。
代码:
public class linkListHomeWork {
public static void main(String[] args) {
//链表初始化
ListNode head = initList();
printListval(head);
//反转链表
System.out.println();
ListNode versehead = reverseList(head);
printListval(versehead);
//链表加一
ListADD(versehead,0);
//链表反转
System.out.println();
ListNode versehead2 = reverseList(versehead);
printListval(versehead2);
}
//打印链表的值
public static void printListval(ListNode head)
{
ListNode temp = head;
while(temp!=null)
{
System.out.print("{"+temp.val+"}");
if(temp.next!=null)
System.out.print(",");;
temp=temp.next;
}
}
//初始化方法(单链表测试)
public static ListNode initList(){
ListNode head = new ListNode(9);
ListNode list = head;
for (int i = 2; i < 10; i++){
ListNode cur = new ListNode(9);
list.next=cur;
list= list.next;
}
return head;
}
//高位表尾单链表加一
public static void ListADD (ListNode cur,int carry)
{
cur.val = cur.val+1;
carry = cur.val / 10 ;
cur.val = cur.val%10;
if(carry==1&&cur.next!=null) {
ListADD(cur.next, carry);
}else if(carry==1&&cur.next==null)
{
ListNode node = new ListNode(1);
cur.next =node;
}
}
//递归反转整个链表
public static ListNode reverseList(ListNode head)
{
if(head == null||head.next==null)
return head;
ListNode newhead = reverseList(head.next);
head.next.next=head;
head.next=null;
return newhead;
}
}
//结点结构
class ListNode {
String name;
String language;
int val;
ListNode next;
ListNode() {
}
ListNode(int val)
{
this.val = val;
}
ListNode(String name, String language) {
this.name = name;
this.language = language;
}
ListNode(String name, String language, int val,ListNode next) {
this.name = name;
this.language = language;
this.next = next;
this.val=val;
}
}