面试常见链表题总结(如果失眠了就拿出来看看)
作者:互联网
无头单链表 现场手撕代码+分析,供自己闲时手机复习使用!
/*
力扣和牛客的部分链表练习题
*/
class ListNode {
public int data = 0;
public ListNode next = null;
public ListNode(int data) {
this.data = data;
}
}
public class List001 {
/*
1.删除链表中等于给定值为val的所有结点
*/
public ListNode removeElements(ListNode head, int val) {
//空链表
if (head == null) {
return null;
}
//一般情况非头结点
ListNode prev = head;
ListNode node = head.next;
while (node != null) {
if (node.data == val) {
prev.next = node.next;
node = prev.next;
} else {
//找完链表都没找到val
prev = node;
node = node.next;
}
}
//删除的正好是头结点
if (head.data == val) {
head = head.next;
}
return head;
}
/*
2.反转一个单链表
*/
public ListNode reverse(ListNode head) {
//空链表
if (head == null) {
return null;
}
//对于只有一个元素情况
if (head.next == null) {
return head;
}
//如果有多个结点
ListNode newHead = null;
ListNode cur = head;
ListNode prev = null;
while (cur != null) {
ListNode next = cur.next;
if (next == null) {
//此时cur已经指向最后一个结点了
newHead = cur;
}
//翻转cur引用的指向
cur.next = prev;
//维护prev和cur
prev = cur;
cur = next;
}
return newHead;
}
/*
3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点
如果有两个中间结点,那么返回第二个中间结点
*/
public ListNode middleNode(ListNode head) {
ListNode cur = head;
int steps = size(head) / 2;
for (int i = 0; i < steps; i++) {
cur = cur.next;
}
return cur;
}
/*
4.输入一个链表,输出该链表中倒数第k个结点
*/
public ListNode FindKthToTail(ListNode head, int k) {
int len = size(head);
//非法情况
if (head == null || k <= 0 || k > len) {
return null;
}
//倒数第K个,也就是正数第len-k个
int offset = len - k;
ListNode cur = head;
for (int i = 0; i < offset; i++) {
cur = cur.next;
}
return cur;
}
/*
5.将两个有序链表合并为一个新的有序链表并返回
新链表是通过拼接给定的两个链表的所有结点组成的
*/
public ListNode mergeTwoLists(ListNode L1,ListNode L2){
//合并链表的话,如果一个为空,则只剩下另一个链表
if(L1 == null){
return L2;
}
if(L2 == null){
return L1;
}
//一般情况
ListNode cur1 = L1;
ListNode cur2 = L2;
ListNode newHead = null;
ListNode newTail = null;
while (cur1 != null && cur2 != null){
if(cur1.data < cur2.data){
//先把cur1指向的结点插在新链表的尾端
//如果新链表为空,直接插
if(newHead == null){
newHead = cur1;
newTail = cur1;
cur1 = cur1.next;
}else{
//不是空链表就续在后面
newTail.next = cur1;
//更新尾部指向
newTail = newTail.next;
cur1 = cur1.next;
}
}else{
//如果cur2比cur1小,则把cur2插在尾端
if(newHead == null){
newHead = cur2;
newTail = cur2;
cur2 = cur2.next;
}else{
newTail.next = cur2;
newTail = newTail.next;
cur2 = cur2.next;
}
}
}
if(cur1 == null){
//L1元素已经完了,L2有剩余,直接放进去就好了
newTail.next = cur2;
}else{
//L2元素已经完了,L1有剩余,直接放进去就好了
newTail.next = cur1;
}
return newHead;
}
/*
6.编写代码,以给定值x为基准将链表分割成两部分,
所有小于x的结点排在大于或等于x的结点之前
*/
public ListNode partition(ListNode pHead,int x){
//空链表
if(pHead == null){
return null;
}
//只有一个元素
if(pHead.next == null){
return pHead;
}
//定义一个只含有傀儡节点的链表放小于x的结点
ListNode smallHead = new ListNode(-1);
ListNode smallTail = smallHead;
//定义一个只含有傀儡节点的链表放大于x的结点
ListNode bigHead = new ListNode(-1);
ListNode bigTail = bigHead;
//遍历原来的链表
for(ListNode cur = pHead; cur != null; cur = cur.next){
//比较cur和x的大小
if(cur.data < x){
//小于x的话,插在smallTail之后
smallTail.next = new ListNode(cur.data);
//维护一下smallTail
smallTail = smallTail.next;
}else{
//大于x的话,插在bigTail之后
bigTail.next = new ListNode(cur.data);
//维护一下bigTail
bigTail = bigTail.next;
}
}
//小的结点在前一个链表,大的结点在后一个链表
smallTail.next = bigHead.next; //小链表尾部接大链表头部
return smallHead.next;
}
/*
7.在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,
重复的结点不保留,返回链表头指针
*/
public ListNode deleteDuplication(ListNode pHead){
//创建一个带有傀儡结点的新链表,用来放不重复的结点
ListNode newHead = new ListNode(-1);
ListNode newTail = newHead;
ListNode cur = pHead;
while(cur != null){
//cur和cur.next都不为null的时候,如果cur的值等于cur.next的值
//那么此时cur就是一个重复结点
if(cur.next !=null && cur.data == cur.next.data){
//此时让cur继续往后走就是为了跳过相同的结点
while (cur.next !=null && cur.data == cur.next.data){
cur = cur.next;
}
//循环结束之后,cur指向的是重复元素的最后一个
//再多走一次,就跳过了所有重复的元素
cur = cur.next;
}else{
//这个结点不是重复的,那么就插在新链表就可以了
newTail.next = new ListNode(cur.data);
newTail = newTail.next;
cur = cur.next;
}
}
return newHead.next; // 跳过那个傀儡节点,返回链表
}
/*
8.链表的回文结构
*/
public boolean chkPalindrome(ListNode A){
//假设空链表也是回文结构
if(A == null){
return true;
}
//一个结点一定是回文
if(A.next == null){
return true;
}
//找中间结点
int len = size(A)/2;
ListNode B = A;
for (int i = 0; i < len; i++) {
B = B.next;
}
//反转原来链表中间结点之后的部分
ListNode prev = null;
ListNode cur = B;
while (cur != null){
ListNode next = cur.next;
if(next == null){
//把B指向新的链表头部
B = cur;
}
cur.next = prev;
//维护prev和cur
prev = cur;
cur = next;
}
//比较两部分是否相同,如果相同那么就是回文
while (B != null){
if(A.data != B.data){
return false;
}
//不断往后比较直到结束
A = A.next;
B = B.next;
}
return true;
}
/*
9.输入两个链表,找出它们的第一个公共结点
*/
public ListNode getIntersectionNode(ListNode headA,ListNode headB){
//分别求两个链表的长度
int lenA = size(headA);
int lenB = size(headB);
//让相对比较长的链表先走差值步数,然后再一起走
if(lenA > lenB){
int offset = lenA - lenB;
for (int i = 0; i < offset; i++) {
headA = headA.next;
}
}else{
int offset = lenB - lenA;
for (int i = 0; i < offset; i++) {
headB = headB.next;
}
}
//分别让两个链表的结点同时移动,比较是否是相同结点
while (headA != null && headB != null){
//比较地址即可
if(headA.equals(headB)){
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
/*
10.给定一个链表,判断链表中是否有环。
*/
public boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
/*
11.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
*/
public ListNode detectCycle(ListNode head) {
// 从链表头部出发, 到入口点的距离,
// 和从快慢指针的交汇处出发, 到入口点的距离相等
if (head == null) {
return null;
}
ListNode fast = head;
ListNode slow = 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;
}
// 循环结束之后, fast 和 slow 就已经重合了
ListNode cur1 = head;
ListNode cur2 = fast;
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
/*
12.获取链表的长度
*/
public int size(ListNode head) {
int size = 0;
for (ListNode cur = head; cur != null; cur = cur.next) {
size++;
}
return size;
}
}
标签:head,ListNode,cur,失眠,next,链表,面试,null 来源: https://blog.csdn.net/WalterBrain/article/details/102750210