链表反转拓展

1.指定区间反转

leetcode92:给你一个单链表和两个整数left,right,其中left<=right,请你反转从Left到right的链表节点,并返回反转后的链表

1.1头插法(带头结点的反转)

public ListNode reverseBetween(ListNode head,int left,int right){
    //创建虚拟节点
    ListNode dummy = new ListNode(-1);
    dummy.next = head;
    ListNode pre = dummy;
    //将pre指针移动到left前一个位置
    for(int i = 0; i < left - 1; i++){
        pre = pre.next;
    }
    //定位left位置
    ListNode cur = pre.next;
    ListNode next;
    //移动节点
    for(int i = 0;i < right - left;i++){
        next = cur.next;
        cur.next = next.next;
        //注意这里不可以是next.next = cur,因为cur一直指向最开始left位置的节点
        next.next = pre.next;
        pre.next = next;
    }
    return dummy.next;
}
1.2穿针引线法(不带头结点的反转)
public ListNode reverseBetween(ListNode head,int left,int right){
    ListNode dummy = new ListNode(-1);
    dummy.next = head;
    ListNode pre = dummy;
    //从虚拟节点走left-1步,来到left的前一个结点
    for(int i = 0;i < left - 1;i++){
        pre = pre.next;
    }
    ListNode rightNode = pre;
    //再从pre走right-left+1步,来到right节点
    for(int i = 0;i < right - left + 1;i++){
        rightNode = rightNode.next;
    }
    //切出一个子链表
    ListNode leftNode = pre.next;
    ListNode succ = rightNode.next;
    rightNode.next = null;
    //链表反转
    reverseLinkedList(leftNode);
    //接回原来的链表
    pre.next = rightNode;
    leftNode.next = succ;
    return dummy.next;
}
public void reverseLinkedList(ListNode head){
    ListNode pre = null;
	ListNode cur = head;
	while(cur != null){
        ListNode next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
	return pre;
}

2.两两交换链表中的节点

leetcode24:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头结点。

public ListNode swapPairs(ListNode head){
    ListNode dummy = new ListNode(0);
	dummy.next = head;
	ListNode temp = dummy;
	while(temp.next != null & temp.next.next != null){
        ListNode node1 = temp.next;
        ListNode node2 = temp.next.next;
        temp.next = node2;
        node1.next = node2.next;
        node2.next = node1;
        temp = node1;
    }
	return dummy.next;
}

3.单链表加1

使用「竖式」计算十进制的加法的方式:

1.两个「加数」的右端对齐;

2.从最右侧开始,从右向左依次计算对应的两位数字的和,如果有进位需要加上进位。如果和大于等于 10,则把和的个位数字计入结果,并向前面进位;

3.重复步骤 2;

4.当两个「加数」的每个位置都计算完成,如果最后仍有进位,需要把进位数字保留到计算结果中。

在实现中需要注意的有:

1.不可以把链表/字符串表示的「加数」先转化成 int 型数字再求和,因为可能溢出;

2.两个「加数」的字符串长度可能不同;

3.在最后,如果进位 carry 不为 0,那么最后需要计算进位。

4.注意 结果数字 是否为低位结果在前,根据题目要求判断最后是否要反转结果。

思路:

本题中给出的链表表示的数字是个位在后,高位在前。

因为加法需要从个位数开始相加,而链表的遍历是从头部(十进制的高位)开始的,因此我们需要把链表翻转过来。

那么就有了两种思路:

思路一:使用栈保存链表中的数字。(栈是先进后出的,所以起到了翻转功能)

步骤:

1.先把题目给出的链表遍历放到栈中;

2.从栈中弹出栈顶数字 digit,计算 adder 之和(adder 在初始化的时候是 1,之后都是 0;表示链表与 1 相加),再加上进位 carry,得到当前位置的和 sum。

如果 sum >= 10 ,那么进位 carry = 1 ,当前位设置为 sum - 10。

如果 sum < 10,那么进位 carry = 0,当前位设置为 sum。

3.设置新链表节点,其值为 sum,逆序拼接成链表即可。

代码中的巧妙之处:

1.while (!st.empty() || adder != 0 || carry > 0)含义:

栈中的元素没遍历完,或者 adder 不为 0,那么就继续遍历;

如果栈遍历完了,但是最后留下的进位 carry != 0,那么需要把进位也保留到结果中。

2.取栈顶元素的时候,如果栈已经遍历完了(即st.empty() ),则认为当前的加数是 0

3.逆序拼接链表的方法:先定义了一个虚拟结点dummy ,然后每次把新构建的链表结点放到 dummy 和 dummy->next 之间,最后返回结果是 dummy->next 。

public ListNode	plusOne(ListNode head){
	Stack<Intger> st = new Stack();
	while(head != null){
        st.push(head.val);
        head = head.next;
    }
	int carry = 0;
	ListNode dummy = new ListNode(0);
	int adder = 1;
	while(!st.empty() || carry>0){
        int digit = st.empty() ? 0 : st.pop();
        int sum = digit + adder + carry;
        carry = sum >= 10 ? 1 : 0;
        sum = sum >= 10 ? sum -10 : sum;
        ListNode cur = new ListNode(sum);
        cur.next = dummy.next;
        dummy.next = cur;
        adder = 0;
    }
    return dummy.next;
}


思路二:反转链表。

public ListNode plusOneByReverseList(ListNode head){
    head = reverseList(head);
	ListNode dummy = new ListNode(0,head);
	ListNode cur = dummy;
	int carry = 0;
	int adder = 1;
	while(head != null || carry > 0){
        int digit = cur.next.val.empty ? 0 :cur.next.val;
        int sum = digit + adder + carry;
        carry = sum >= 10 ? 1 : 0;
        sum = sum >= 10 ? sum - 10 : sum;
        ListNode cur = new ListNode(sum);
        cur.next = dummy.next;
        dummy.next = cur;
        adder = 0;
    }
	return reverseList(dummy.next);
}
public ListNode reverseList(ListNode head){
    ListNode pre = null;
    ListNode cur = head;
    while(cur != null){
        ListNode next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

4.链表加法

相加链表是基于链表构造的一种特殊题,反转只是其中的一部分,这个题还存在进位的问题。

leetcode445:给你两个非空链表来代表两个非负整数,数字最高位位于链表开始位置,它们的每个节点只存储一位数字,将这两数相加返回一个新的链表。你可以假设除了数字0之外,这两个数字都不会以0开头

这个题目的难点在于存放是从最高位向最低位开始的,但是因为低位会产生进位的问题,计算的时候必须从最低位开始,所以想办法将链表的节点反转过来

(1)使用栈实现

思路:先将两个链表的元素分别压栈,然后一起出栈,将两个结果分别计算,之后对计算结果取模,余数保存到新的链表中,进位保存到下一轮,完成后再进行一次反转就行。

public static ListNode addInListByStack(ListNode head1,ListNode head2){
    Stack<ListNode> st1 = new Stack<ListNode>();
    Stack<ListNode> st2 = new Stack<ListNode>();
    //分别入栈
    while(head1 != null){
        st1.push(head1);
        head1 = head1.next;
    }
    while(head2 != null){
        st2.push(head2);
        head1 = head2.next;
    }
    ListNode newHead = new ListNode(-1);
    int carry = 0;
    while(!st1.empty() || !st2.empty() || carry != 0){
        ListNode a = new ListNode(0);
        ListNode b = new ListNode(0);
        if(!st1.empty()){
            a = st1.pop();
        }
        if(!st2.empty()){
            b = st2.pop();
        }
        //每次的和应该是对应位相加再加上进位
        int get_sum = a.val + b.val +carry;
        //对累加结果取余
        int ans = get_sum % 10;
        //如果大于10,就进位
        carry = get_sum / 10;
        //用头插法将每最新得到的节点插入newHead.next中
        ListNode cur = new ListNode(ans);
        cur.next = newHead.next;
        newHead.next = cur;
    }
    return newHead.next;
}

(2)使用链表反转实现

思路:先将两个链表分别反转,最后计算完之后再将结果反转,将反转抽取出一个方法

public class Solution{
    public ListNode addInList(ListNode head1,ListNode head2){
        head1 = reverse(head1);
        head2 = reverse(head2);
        ListNode head = new ListNode(-1);
        ListNode cur = head;
        int carry = 0;
        while(head1 != null || head2 != null){
            int val = carry;
            if(head1 != null){
                val += head1.val;
                head1 = head1.next;
            }
            if(head2 != null){
                val += head2.val;
                head2 = head2.next;
            }
            cur.next = new ListNode(val % 10);
            carry = val / 10;
            cur = cur.next;
        }
        if(carry > 0){
            cur.next = new ListNode(carry);
        }
        return reverse(head.next);
    }
    private ListNode reverse(ListNode head){
        ListNode cur = head;
        ListNode pre = null;
        while(cur != null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

5.再论链表的回文序列问题

思路:“快慢指针+反转一半”

public boolean isPalindrome(ListNode head){
    if(head == null || head.next ==null){
        return ture;
    }
	ListNode slow = head,fast = head;
	ListNode pre = head,prepre = null;
	while(fast != null || fast.next != null){
        pre = slow;
        slow = slow.next;
        fast = fast.next.next;
        //反转前半部分链表
        pre.next = prepre;
        prepre.next = pre;
    }
	if(fast != null){
        slow = slow.next;
    }
	while(pre != null && slow != null){
        if(pre.val != slow.val){
            return false;
        }
        pre = pre.next;
        slow = slow.next;
    }
	return ture;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值