请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。
1.先判断头节点是否为待删除的节点(用while删除前面所有值为val的节点)
2.删除完头节点之后,对链表判空
3.删除链表中间值为val的节点(注意创建新节点保存原链表中的节点)
public static ListNode removeElements(ListNode head, int val) {
while (head.val==val){
// 当头节点为待删除的节点时,删除头节点,直到头节点不为val
head=head.next;
}
if(head==null){
// 头节点删除完了之后,需要判空。因为有可能链表所有的节点都是待删除的节点
return null;
}else{
// 此时链表中间的节点值为待删除的节点值
ListNode prev=head;
for (ListNode cur = prev.next; cur != null ; cur=cur.next) {
if(cur.val==val){
// 此时遍历到的节点为待删除的节点,此时需要判断是否为最后一个节点
if(cur.next==null){
prev.next=null;
}else{
prev.next=cur.next;
cur.next=null;
}
}else{
prev=prev.next;
}
}
}
return head;
}
完整测试+方法代码
package List;
/**
* 203. 移除链表元素
* 给你一个链表的头节点 head 和一个整数 val ,
* 请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
*/
public class Num203_removeElements {
public static class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Num203_removeElements num203=new Num203_removeElements();
ListNode newhead=num203.removeElements(head,6);
System.out.println(newhead);
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(2);
ListNode node3=new ListNode(6);
ListNode node4=new ListNode(3);
ListNode node5=new ListNode(4);
ListNode node6=new ListNode(5);
ListNode node7=new ListNode(6);
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node6;
node6.next=node7;
return node1;
}
public static ListNode removeElements(ListNode head, int val) {
while (head.val==val){
// 当头节点为待删除的节点时,删除头节点,直到头节点不为val
head=head.next;
}
if(head==null){
// 头节点删除完了之后,需要判空。因为有可能链表所有的节点都是待删除的节点
return null;
}else{
// 此时链表中间的节点值为待删除的节点值
ListNode prev=head;
for (ListNode cur = prev.next; cur != null ; cur=cur.next) {
if(cur.val==val){
// 此时遍历到的节点为待删除的节点,此时需要判断是否为最后一个节点
if(cur.next==null){
prev.next=null;
}else{
prev.next=cur.next;
cur.next=null;
}
}else{
prev=prev.next;
}
}
}
return head;
}
}
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
package List;
/**
* 83. 删除排序链表中的重复元素
* 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。
* 返回 已排序的链表 。
*/
public class Num83_deleteDuplicates {
public static class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public static ListNode deleteDuplicates(ListNode head) {
if(head==null || head.next==null){
return head;
}
ListNode tmp=head.next;
ListNode newhead=head;
while (tmp!=null){
while (tmp!=null && head.val==tmp.val){
tmp=tmp.next;
}
head.next=tmp;
if(tmp==null){
break;
}
head=head.next;
tmp=tmp.next;
}
return newhead;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Num83_deleteDuplicates Num83=new Num83_deleteDuplicates();
System.out.println(deleteDuplicates(head));
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(1);
ListNode node3=new ListNode(2);
ListNode node4=new ListNode(3);
ListNode node5=new ListNode(3);
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
return node1;
}
}
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]
package List;
import com.sun.xml.internal.ws.api.ha.HaInfo;
import java.util.HashMap;
import java.util.Map;
/**
* 面试题 02.01. 移除重复节点
* 编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
* 输入:[1, 2, 3, 3, 2, 1]
* 输出:[1, 2, 3]
* 思路:
* 将遍历到的节点值(不重复)保存在哈希表中,
* 当最新遍历到的节点值在哈希表中,移除
* 当不在哈希表中,向后移动
*/
public class Num0201_removeDuplicateNodes {
public static class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
public static ListNode removeDuplicateNodes(ListNode head) {
if(head==null || head.next==null){
return head;
}
Map<Integer,Integer> map=new HashMap<>();
map.put(head.val,1);
ListNode newhead=head;
ListNode tmp=head.next;
while (tmp!=null){
if(!map.containsKey(tmp.val)){
map.put(tmp.val,1);
head.next=tmp;
head=head.next;
tmp=tmp.next;
}else{
tmp=tmp.next;
}
}
head.next=null;
return newhead;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Num0201_removeDuplicateNodes Num83=new Num0201_removeDuplicateNodes();
System.out.println(removeDuplicateNodes(head));
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(2);
ListNode node3=new ListNode(3);
ListNode node4=new ListNode(3);
ListNode node5=new ListNode(2);
ListNode node6=new ListNode(1);
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node6;
return node1;
}
}
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
创建新链表,遍历原链表,将遍历到的节点头插到新链表中。最后返回虚拟头节点的下一个节点
将遍历到的节点头插到dummyhead虚拟头节点的后面
第二个节点
package List;
/**
* 206. 反转链表
* 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
*
*/
public class Num206_reverseList {
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public ListNode reverseList(ListNode head) {
if(head==null || head.next==null){
// 链表为空,或者链表中只有一个元素
return head;
}
// 此时链表中至少两个元素
// 创建新链表的虚拟头节点
ListNode dummyhead=new ListNode(5001);
// 遍历原链表
while (head!=null){
ListNode node=new ListNode(head.val);
node.next=dummyhead.next;
dummyhead.next=node;
head=head.next;
}
return dummyhead.next;
}
}
public ListNode reverseList1(ListNode head) {
if(head==null || head.next==null){
// 链表为空,或者链表中只有一个元素
return head;
}
ListNode prev=null;
ListNode cur=head;
while (cur!=null){
ListNode next=cur.next;
cur.next=prev;
prev=cur;
cur=next;
}
return prev;
}
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/middle-of-the-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路一:先遍历链表,看链表中有多少个节点count,再计算中间节点count=count/2+1;
再遍历一遍链表,每遍历一个节点,count–,当count==0时,返回
public ListNode middleNode(ListNode head) {
int count=0; // 计算链表中有多少个节点
ListNode cur=head;
while (cur!=null){
count++;
cur=cur.next;
}
count=(count/2)+1; // 计算中间节点
while (head!=null){
count--;
if(count==0){
break;
}else{
head=head.next;
}
}
return head;
}
思路2:快慢指针,定义两个指针fast low ,fast一次走两步,low一次走一步。当fast走到最后为null时,low为中间节点
public ListNode middleNode1(ListNode head) {
ListNode fast=head;
ListNode low=head;
while (fast!=null && fast.next!=null){
fast=fast.next.next;
low=low.next;
}
return low;
}
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
思路1:
先遍历一遍链表计算链表中的节点个数count,
再遍历一遍链表当走到count-k+1节点时 返回
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode cur=head;
int count=0;
while (cur!=null){
count++;
cur=cur.next;
}
count=count-k+1;
while (head!=null){
count--;
if(count==0){
break;
}else{
head=head.next;
}
}
return head;
}
思路2:快慢指针
定义两个指针 fast low
fast先走K步,low再开始走,保证fast和low的相对距离为k,当fast为null时,返回low
public ListNode getKthFromEnd1(ListNode head, int k) {
ListNode fast=head;
ListNode low=head;
while (k!=0){
fast=fast.next;
k--;
}
while (fast!=null){
fast=fast.next;
low=low.next;
}
return low;
}
给定一个链表的 头节点 head ,请判断其是否为回文链表。
如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
思路1:
1.创建新链表,将原链表进行转置
2.同时遍历原链表和新建的链表,当遍历到的值不同时,return false
两个链表遍历完,return true
package List;
/**
* 剑指 Offer II 027. 回文链表
* 给定一个链表的 头节点 head ,请判断其是否为回文链表。
* 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
*先将链表进行反转,再判断反转后的链表和原链表是否相等
*/
public class Offer027_isPalindrome {
public static class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public static boolean isPalindrome(ListNode head) {
ListNode head1=reverseList(head);
while (head1!=null){
if(head.val!=head1.val){
return false;
}
head=head.next;
head1=head1.next;
}
return true;
}
// 反转链表
public static ListNode reverseList(ListNode head){
if(head==null || head.next==null){
return head;
}
ListNode dummyhead=new ListNode(10);
while (head!=null){
ListNode node=new ListNode(head.val);
node.next=dummyhead.next;
dummyhead.next=node;
head=head.next;
}
return dummyhead.next;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Offer027_isPalindrome offer027=new Offer027_isPalindrome();
System.out.println(isPalindrome(head));
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(1);
ListNode node3=new ListNode(2);
ListNode node4=new ListNode(1);
node1.next=node2;
node2.next=node3;
node3.next=node4;
return node1;
}
}
思路2
1.先找到原链表的中间节点
2.将中间节点之后的子链表进行反转
同时遍历原链表和反转后的子链表,注意便遍历满足的条件是以遍历子链表的长度
package List;
/**
* 剑指 Offer II 027. 回文链表
* 给定一个链表的 头节点 head ,请判断其是否为回文链表。
* 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
*先将链表进行反转,再判断反转后的链表和原链表是否相等
*/
public class Offer027_isPalindrome {
public static class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public static boolean isPalindrome1(ListNode head) {
ListNode middlenode=middleNode(head);
ListNode reverse_middlenode=reverseList(middlenode);
while (reverse_middlenode!=null){
if(head.val!=reverse_middlenode.val){
return false;
}
head=head.next;
reverse_middlenode=reverse_middlenode.next;
}
return true;
}
// 反转链表
public static ListNode reverseList(ListNode head){
if(head==null || head.next==null){
return head;
}
ListNode dummyhead=new ListNode(10);
while (head!=null){
ListNode node=new ListNode(head.val);
node.next=dummyhead.next;
dummyhead.next=node;
head=head.next;
}
return dummyhead.next;
}
// 寻找链表的中间节点
public static ListNode middleNode(ListNode head){
while (head==null||head.next==null){
return head;
}
ListNode fast=head;
ListNode low=head;
while (fast.next!=null){
fast=fast.next.next;
low=low.next;
}
return low;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Offer027_isPalindrome offer027=new Offer027_isPalindrome();
System.out.println(isPalindrome1(head));
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(0);
ListNode node3=new ListNode(0);
node1.next=node2;
node2.next=node3;
return node1;
}
}
思路3 将值复制到数组中后用双指针法
1.复制链表值到数组列表中。
2.使用双指针法判断是否为回文。
package List;
import java.util.ArrayList;
import java.util.List;
/**
* 剑指 Offer II 027. 回文链表
* 给定一个链表的 头节点 head ,请判断其是否为回文链表。
* 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
*先将链表进行反转,再判断反转后的链表和原链表是否相等
*/
public class Offer027_isPalindrome {
public static class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public static boolean isPalindrome2(ListNode head) {
List<Integer> list=new ArrayList<>();
while (head!=null){
list.add(head.val);
head=head.next;
}
int left=0;
int right=list.size()-1;
while (left<right){
if(list.get(left)!=list.get(right)){
return false;
}
left++;
right--;
}
return true;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build();
Offer027_isPalindrome offer027=new Offer027_isPalindrome();
System.out.println(isPalindrome2(head));
}
// 创建链表
public static ListNode build(){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(0);
ListNode node3=new ListNode(0);
node1.next=node2;
node2.next=node3;
return node1;
}
}
快慢指针法:试想两个人在环形跑道跑步,一个人跑的快,一个人跑的慢,两个人总会相遇。
1.定义两个指针fast low
2.fast每次走两步,low每次走一步,条件:当fast!=null时
如果fast和low相遇,则带环
public boolean hasCycle(ListNode head) {
if(head==null){
return false;
}
ListNode fast=head;
ListNode low=head;
while (fast!=null && fast.next!=null){
fast=fast.next.next;
low=low.next;
if(fast==low){
return true;
}
}
return false;
}
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
创建新链表,遍历两个链表同时比较两个链表的值大小,将值较小的链表节点尾插到新链表中。
package List;
/**
* 剑指 Offer 25. 合并两个排序的链表
* 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
* 输入:1->2->4,
* 1->3->4
* 输出:1->1->2->3->4->4
*/
public class Offer25_mergeTwoLists {
public static class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode dummyhead=new ListNode(101);
ListNode cur=dummyhead;
while (list1!=null || list2!=null){
if(list1!=null){
if(list2==null||(list1.val<=list2.val)){
ListNode node=new ListNode(list1.val);
dummyhead.next=node;
dummyhead=dummyhead.next;
list1=list1.next;
}else{
ListNode node=new ListNode(list2.val);
dummyhead.next=node;
dummyhead=dummyhead.next;
list2=list2.next;
}
}else if(list2!=null){
ListNode node=new ListNode(list2.val);
dummyhead.next=node;
dummyhead=dummyhead.next;
list2=list2.next;
}
}
return cur.next;
}
public static void main(String[] args) {
// 创建链表[1,2,6,3,4,5,6]
ListNode head=build1();
ListNode head1=build2();
Offer25_mergeTwoLists offer25=new Offer25_mergeTwoLists();
System.out.println(mergeTwoLists(head,head1));
}
// 创建链表
public static ListNode build1(){
ListNode node1=new ListNode(2);
return node1;
}
public static ListNode build2(){
ListNode node1=new ListNode(1);
return node1;
}
}
这样更好理解
public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode dummyhead=new ListNode(101);
ListNode cur=dummyhead;
while (list1!=null && list2!=null){
if(list1.val<=list2.val){
ListNode node=new ListNode(list1.val);
dummyhead.next=node;
dummyhead=dummyhead.next;
list1=list1.next;
}else{
ListNode node=new ListNode(list2.val);
dummyhead.next=node;
dummyhead=dummyhead.next;
list2=list2.next;
}
}
if(list1==null){
dummyhead.next=list2;
}else if(list2==null){
dummyhead.next=list1;
}
return cur.next;
}