目录
1.OJ:移除链表元素
2.OJ:链表中倒数第K个节点【双指针问题】
3.OJ:判断链表是否有环【双指针】
4.OJ:删除链表中重复的节点
5.OJ:删除重复的节点
6.OJ:合并两个有序链表
7.OJ:找到链表的中间节点
8.OJ:反转链表
9.OJ:相交链表
10.OJ:合并两个有序链表
11.OJ:链表的回文结构
12.OJ:两个链表第一个公共节点
13.OJ:环形链表
1. 删除链表中等于给定值的所有链表(返回一个新链表)
思路:定义一个游标cur = head,生成一个新的节点,当cur.val != val时,加入到新生节点的后面,否则,继续下一个。注意(如果cur == null,要将temp。next置为空)
/**
* Definition for singly-linked list.
* 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; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode cur = head;
ListNode newHead = new ListNode(-1);
ListNode temp = newHead;
if(head == null){
return null;
}
while(cur != null){
if(cur.val != val){
temp.next = cur;
temp = temp.next;
cur = cur.next;
}else{
cur = cur.next;
}
}
if(cur == null){
temp.next = null;
}
return newHead.next;
}
}
2.找到链表中倒数第k个节点
方法一也是可以实现删除倒数第k个节点,但是需要遍历链表的长度来检验k值得合法性,所以复杂度比较高。
思路:
定义两个指针fast 和 slow,让fast 多走k-1步,然后slow接着走,当fast.next为空的时候,slow对应的位置就是倒数第k个节点的位置。
比如,有四个节点,要找倒数第2个, 先让fast指针多走一步,然后fast和slow同时走,当fast.next为空的时候,slow对应的就是要找的节点。
方法二是在方法一的基础上进行了优化。
还是刚才的例子,只有四个节点,但是要找倒数第5个节点,倒数第5个节点肯定不存在,在fast多走了3步之后,发现fast.next为空就会退出循环。
public class MyLinkedList {
static class Node{
public int data; //默认为0
public Node next;//默认为null
public Node(int data){
this.data = data;
this.next = null;
}
}
//找到链表中倒数第k个节点
public MyListNode FindKthToTail(int k){
//方法一:
// Node slow = this.head;
// Node fast = this.head;
// if(k <= 0 || k > size()){
// System.out.println("k不合法!");
// return null;
// }
//
// while(k - 1 > 0){
// fast = fast.next;
// k--;
// }
// while(fast.next != null){
// fast = fast.next;
// slow = slow.next;
// }
// return slow;
//方法二:优化
if(this.head == null){
return null;
}
if(k <= 0){
System.out.println("k的位置不合法!");
return null;
}
Node fast = this.head;
Node slow = this.head;
while(k-1 >0){
if(fast.next != null){
fast = fast.next;
k--;
}else{
System.out.println("没有这个节点");
return null;
}
}
while(fast.next != null){
fast = fast.next;
slow =slow.next;
}
return slow;
}
}
3.链表中是否有环
思路:定义fast和slow,当fast.next != null 的时候(也就是没有到最后一个节点的时候),
如果fast 和 slow 相遇,那么说明有环。
public boolean hasCycle() {
MyListNode fast = this.head;
MyListNode slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
return true;
}
}
return false;
}
4. 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 还要返回链表头。
思路:定义一个新的虚拟节点,如果cur.val == cur.next.val,一直找到一个不相等的,插入到新的节点(注意:cur.next!=null)
public MyListNode deleteDuplication() {
MyListNode newHead = new MyListNode(-1);
MyListNode temp = newHead;
MyListNode cur = this.head;
while (cur != null) {
if(cur.next!=null && cur.val == cur.next.val){
while(cur.next!=null && cur.val == cur.next.val){
cur = cur.next;
}
cur = cur.next;
} else{
temp.next = cur;
temp = temp.next;
cur = cur.next;
}
}
temp.next = null;
return newHead.next;
}
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,不用返回新链表。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->3->4->5
public void deleteDuplication2() {
MyListNode cur = this.head;
MyListNode curNext = cur.next;
while(curNext != null){
if(cur.val == curNext.val){
cur.next = curNext.next;
curNext = curNext.next;
} else{
cur = curNext;
curNext = cur.next;
}
}
}
5.删除重复的节点
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:
输入:[1, 2, 3, 3, 2, 1] 输出:[1, 2, 3]
public ListNode removeDuplicateNodes(ListNode head) {
HashSet<Integer> set= new HashSet();
ListNode prev = null;
ListNode cur = head;
while(cur != null){
if(set.contains(cur.val)){
prev.next = cur.next;
cur = cur.next;
}else{
set.add(cur.val);
prev = cur;
cur = cur.next;
}
}
return head;
}
6.合并两个顺序有序链表
//合并两个顺序有序链表
public void mergeTwo(MyListNode headA,MyListNode headB){
MyListNode newHead = new MyListNode(-1);
MyListNode temp = newHead;
while(headA != null && headB !=null){
if(headA.val < headB.val){
temp.next = headA;
temp = temp.next;
headA = headA.next;
} else{
temp.next = headB;
temp = temp.next;
headB = headB.next;
}
}
//当headA为空,将headB链入
if(headA == null){
temp.next = headB;
}
if(headB == null){
temp.next = headA;
}
}
7.找到链表的中间节点,如果有两个,返回两个中的后一个。
思路:定义一个fast和 slow,fast的速度是slow的2倍。当fast或fast.next为空时,返回slow
public MyListNode middlListNode(){
MyListNode fast = this.head;
MyListNode slow = this.head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
8.反转链表
思路:游标要从前往后走
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode newHead = new ListNode(-1);
ListNode cur = head;
if(head == null){
return null;
}
ListNode curNext = cur.next;
while(cur != null){
curNext= cur.next;
if(curNext== null){
newHead = cur;
}
cur.next = prev;
prev = cur;
cur = curNext;
}
return newHead;
}
9.相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
public static Node getIntersectionNode(Node headA, Node headB){
//1.求长度,走差值步
Node pl = headA;
Node ps = headB;
int lenA = 0;
int lenB = 0;
while(pl != null){
lenA++;
pl = pl.next;
}
while(ps != null){
lenB++;
ps = ps.next;
}
//2.求长度 一定要返回 因为之前的while循环已经到null了
pl =headA;
ps=headB;
int len = lenA- lenB;
if(len < 0){
len = lenB - lenA;
ps = headA;
pl = headB;
}
//一定是pl指定的是长的单链表
for(int i = 0; i < len; i++){
pl = pl.next;
}
//pl和ps 一定在同一个起跑线上
while(ps != pl && pl !=null && ps != null ){
ps = ps.next;
pl = pl.next;
}
if(pl == ps && pl != null){
return ps;
}
return null;
}
10.合并两个有序链表
public Node Merge(Node headA,Node headB){
Node newHead = new Node(-1);
Node tmp = newHead;
while(headA != null && headB !=null){
if(headA.data < headB.data){
tmp.next = headA;
tmp = tmp.next;
headA= headA.next;
}else{
tmp.next =headB;
tmp =tmp.next;
headB = headB.next;
}
}
if(headA != null){
tmp.next =headA;
}
if(headB != null){
tmp.next =headB;
};
return newHead.next;
}