链表
题目 | 知识点 |
---|---|
18:删除链表的节点。 | 链表 |
22:输入一个链表,输出该链表中倒数第k个结点。 | 链表 |
23:链表中环的入口节点。 | 链表 |
24:反转链表。 | 链表 |
25:合并两个排序的链表。 | 链表 |
⭐️⭐️18:删除链表中重复的结点。
题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
题解:
⭐️⭐️22:输入一个链表,输出该链表中倒数第k个结点。
描述:输入一个链表,输出链表在中倒数第K个节点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点.例如,一个链表有6个节点,从头节点开始,他们的值依次是1,2,3,4,5,6.这个链表的倒数第3个节点是值为4的节点.
链表节点定义:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
题解:
本题链表是单向的,无法通过从尾端回溯K步求解.由于,倒数第K个节点就是从头节点开始的第n-k+1个节点,那么只要知道节点总数n即可求解.
遍历链表两次,第一次统计链表中节点的个数,第二次找出倒数第K个节点
注意:(1)head节点不能为空(2)K数不能超过节点总数n
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
ListNode p,q;
//p用来记录节点总数,q用来找到倒数K节点
p=q=head;
if(head==null) return null;
int count=0;
while(p!=null){
count++;
p=p.next;
}
if(count<k) return null;
for(int i=0;i<count-k;i++){
q=q.next;
}
return q;
}
}
上述代码需要遍历两次,可进一步优化.我们可以定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针不动.从第K步开始,第二个指针也开始遍历,当第一个指针到达链表尾节点时第二个指针停止.
public ListNode FindKthToTail(ListNode head,int k) { //5,{1,2,3,4,5}
ListNode p, q;
p = q = head;
int count = 0;
for (; p != null; count++) {
if (count >= k)
q = q.next;
p = p.next;
}
return count< k ? null : q;
}
⭐️23:链表中环的入口节点。
如果一个链表中包含环,如何找出环的入口节点?
题解
快慢指针
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast=pHead;
ListNode slow=pHead;
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=pHead;
while (fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
⭐️24:反转链表
题目:定义一个函数,输入一个链表的头节点,反转该链条并输出反转后的头节点.链表节点定义如下:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
题解
方法一:循环
public ListNode ReverseList(ListNode head) {
if (head==null) return null;
ListNode pre=null;
ListNode next=null;
while (head!=null){
next=head.next;
head.next=pre;
pre=head;
head=next;
}
return pre;
}
方法二:递归调用
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null||head.next ==null)
return head;
ListNode next = head.next;
ListNode prev = ReverseList(next);
head.next.next = head;
head.next = null;
return prev;
}
}
⭐️25:合并两个排序的链表
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点依然是增序排列的.
题解:
当我们得到两个链表中值较小的头节点并把它链接到已合并的链表之后,两个链表剩余的节点依然是升序的,因此合并的步骤和之前的步骤是一样的.这就是典型的递归过程.
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}else if(list2==null){
return list1;
}
ListNode pMergeHead = null;
if(list1.val<list2.val){
pMergeHead = list1;
pMergeHead.next = Merge(list1.next,list2);
}else{
pMergeHead = list2;
pMergeHead.next = Merge(list1,list2.next);
}
return pMergeHead;
}
}
⭐️36:二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
public TreeNode Convert(TreeNode root) {
if(root==null)
return null;
if(root.left==null&&root.right==null)
return root;
// 1.将左子树构造成双链表,并返回链表头节点
TreeNode left = Convert(root.left);
TreeNode p = left;
// 2.定位至左子树双链表最后一个节点
while(p!=null&&p.right!=null){
p = p.right;
}
// 3.如果左子树链表不为空的话,将当前root追加到左子树链表
if(left!=null){
p.right = root;
root.left = p;
}
// 4.将右子树构造成双链表,并返回链表头节点
TreeNode right = Convert(root.right);
// 5.如果右子树链表不为空的话,将该链表追加到root节点之后
if(right!=null){
right.left = root;
root.right = right;
}
return left!=null?left:root;
}
⭐️6:从尾到头打印链表
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
public ArrayList printListFromTailToHead(ListNode listNode) {
ArrayList list = new ArrayList();
Stack stack = new Stack();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
while (!stack.empty()) {
list.add(stack.pop());
}
return list;
}
⭐️35:复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
题解
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead == null) {
return null;
}
RandomListNode currentNode = pHead;
//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
while(currentNode != null){
RandomListNode cloneNode = new RandomListNode(currentNode.label);
RandomListNode nextNode = currentNode.next;
currentNode.next = cloneNode;
cloneNode.next = nextNode;
currentNode = nextNode;
}
currentNode = pHead;
//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(currentNode != null) {
currentNode.next.random = currentNode.random==null?null:currentNode.random.next;
currentNode = currentNode.next.next;
}
//3、拆分链表,将链表拆分为原链表和复制后的链表
currentNode = pHead;
RandomListNode pCloneHead = pHead.next;
while(currentNode != null) {
RandomListNode cloneNode = currentNode.next;
currentNode.next = cloneNode.next;
cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
currentNode = currentNode.next;
}
return pCloneHead;
}
}