1、 删除链表中等于给定值 **val** 的所有节点。
思路分析:
定义两个辅助指针 prev 和 cur,当prev.next.data= val时删除即可
主要是别忘了处理第一个节点
public void removeAllKey(int key) {
ListNode prev = this.head;
ListNode cur = this.head.next;
while(cur != null) {
if (prev.next.data == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
//处理第一个节点--最后处理
if (this.head.data == key) {
this.head = this.head.next;
}
}
prev = cur
public ListNode reverseList() {
if(this.head == null) {
return null;
}
ListNode cur = this.head;
ListNode prev = null;
ListNode newHead = null;
while (cur != null) {
ListNode curNext = cur.next;
if (curNext == null) {
newHead = cur;
}
cur.next = prev;
prev = cur;
cur = curNext;
}
return newHead;
}
public ListNode middleNode() {
// ListNode cur = this.head;
// int length = getlength()/2;
// for (int i = 0; i < length; i++) {
// cur = cur.next;
// }
// return cur;
ListNode fast = this.head;
ListNode slow = this.head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
注意:首先要判断k是否有效,最高效的办法就是最多走节点个数-1步,因此在快指针走的同时加以判断,fast.next = null;说明k不合法返回null即可。
public ListNode FindKthToTail(int k) {
if (k <= 0 || this.head == null) {
return null;
}
ListNode fast = this.head;
ListNode slow = this.head;
for (int i = 0; i < k-1; i++) {
if (fast.next == null) {
return null;
}
fast = fast.next;
}
while(fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
public static ListNode mergeTwoLists1(ListNode headA, ListNode headB){
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
while (headA != null && headB != null) {
if (headA.data < headB.data) {
tmp.next = headA;
headA = headA.next;
tmp = tmp.next;
} else {
tmp.next = headB;
headB = headB.next;
tmp = tmp.next;
}
}
//如果a为空
if (headA == null) {
tmp.next = headB;
} else {
tmp.next = headA;
}
return newHead.next;
}
6、编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
思路分析:
①首先,要有两个引用 beginStart和beginEnd 、afterStart和afterEnd
②当链表中节点的数据比k小时,放入beginStart指向的这个节点,当链表中节点的数据比k大时,放入afterStart指向的这个节点。
放的时候要注意是否为第一次放。
③将beginEnd和afterStart连起来
看起来好像结束了,其实漏掉了两个问题:
a.如果链表中的元素全部都小于k,那么返回bs完全没有问题。。但是如果链表中的元素全都大于k,说明bs为空,那么直接返回as即可,加一个判断。
b.当我们返回bs的时候,会发现打印链表终止条件是null,但是放完链表时,ae.next不一定为空,因此最后我们要把ae.next置为空。 这样的话才可行嘿嘿~
public ListNode partition(int x){
ListNode bs = null;
ListNode be = null;
ListNode as = null;
ListNode ae = null;
ListNode cur = this.head;
while (cur != null) {
if (cur.data < x) {
//判断是否为第一次加入
if (bs == null) {
bs = cur;
be = cur;
} else {
be.next = cur;
be = be.next;
}
} else {
//判断是否为第一次加入
if (as == null) {
as = cur;
ae = cur;
} else {
ae.next = cur;
ae = ae.next;
}
}
cur = cur.next;
}
//连在一起
if (be.next != null) {
be.next = as;
}
//判断是否k是否小于所以节点
if (bs == null) {
return as;
}
if (as != null) {
ae.next = null;
}
return bs;
}
7. 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
思路分析:
①寻找重复节点,定义一个辅助变量cur遍历单链表,如果cur.data = cur.next.data时说明有节点数据重复。但是要注意重复的节点不止一个,因此判断时要用循环。
②删除重复的节点
注意:
a.每次判断判断cur.data == cur.next.data时都要判断cur.next 是否为 null。否则会造成空指针异常。
b.如果最后一个节点也为重复节点,那么要 tmp.next = null;
public ListNode deleteDuplication(){
ListNode cur = this.head;
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
while (cur != null) {
//重复的节点
if(cur.next != null && cur.data == cur.next.data) {
//每一次都需要判断cur.next
while (cur.next != null && cur.data == cur.next.data) {
cur = cur.next;
}
cur = cur.next;
} else {
tmp.next = cur;
tmp = tmp.next;
cur = cur.next;
}
}
//最后一个节点如果也是重复的,需要将tmp.next置为空
tmp.next = null;
return newHead.next;
}
8. 链表的回文结构。
如何判断链表的回文结构呢?
②反转链表
③fast/slow往前 head往后走
//单链表判断回文数
public boolean chkPalindrome() {
//为空
if (this.head == null) {
return false;
}
//一个数
if (head.next == null) {
return true;
}
//1、找到单链表的中间节点
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//2、反转单链表
ListNode cur = slow.next;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
//3、fast/slow往前 head往后走
while (this.head != slow) {
if (this.head.data != slow.data) {
return false;
}
//偶数个数
if (this.head.next == slow) {
return true;
}
this.head = this.head.next;
slow = slow.next;
}
return true;
}
9. 输入两个链表,找出它们的第一个公共结点。
思路分析:
①将pL指向长链表,pL指向短的链表
②让长链表先走差值步
③分别一步一步走,如果节点相同就为相交
注意:
注意计算完长度后pS、pL重新赋值,还有最后循环退出的条件
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) {
return null;
}
ListNode pL = headA;//永远指向长的单链表
ListNode pS = headB;//永远指向短的单链表
int lenA = 0;
int lenB = 0;
//求的lenA lenB
while (pL != null) {
lenA++;
pL = pL.next;
}
while (pS != null) {
lenB++;
pS = pS.next;
}
//pl、ps为空了
pL = headA;
pS = headB;
//差值-》最长的单链表先走len步
int len = lenA-lenB;
if(len < 0) {
pL = headB;
pS = headA;
len = lenB-lenA;
}
//让pL先走len步
for (int i = 0; i < len; i++) {
pL = pL.next;
}
//开始一起走 (pL != pS ) {一人一步走}
while (pL != pS) {
pL = pL.next;
pS = pS.next;
}
// if (pL == null) {
// return null;
// }
// return pL;
return pL;
}
判断链表是否有环可以使用快慢指针法,快的每次走两步,慢的每次走一步。若快的和慢的相遇则有环。
//判断链表是否有环
public boolean hasCycle() {
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
break;
}
}
if (fast == null || fast.next ==null) {
return false;
}
return true;
}
//创造一个环
public void creteLoop() {
ListNode cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = this.head.next;
}
11. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
当slow=fast时,将slow指向头节点。slow和fast同时往后走,当slow和fast相等时的节点就是链表开始入环的第一个节点。
public ListNode detectCycle() {
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
break;
}
}
if (fast == null || fast.next ==null) {
return null;
}
slow = this.head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
证明:画图
12、给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的深拷贝。
思路分析:
①老新进行进行交替链接
②修改random
③将老新节点打开
public Node copyRandomList(Node head) {
if(head == null) {
return null;
}
Node cur = head;
//1、老新进行进行交替链接
while(cur != null) {
Node node = new Node(cur.val, cur.next, null);
Node tmp = cur.next;
cur.next = node;
cur = tmp;
}
//2、修改random
cur = head;
while(cur != null) {
if(cur.random != null) {
cur.next.random = cur.random.next;
cur = cur.next.next;
} else {
cur = cur.next.next;
}
}
//3、将老新节点打开
cur = head;
Node newHead = cur.next;
while(cur.next != null) {
Node tmp = cur.next;
cur.next = tmp.next;
cur = tmp;
}
return newHead;
}
最近做了挺多单链表的题,感觉虽然有的题目思路并不是特别难,但是一不小心就会造成空指针异常,因此做单链表的题要考虑全面,包括单链表的头、尾以及单链表是否为空,next是否为空。。。。