作者:爱塔居的博客_CSDN博客-JavaSE,数据结构领域博主
专栏:数据结构
作者简介:大三学生,希望2023年迎来更优秀的自己!希望跟大家一同进步~
文章目录
前言
一、链表的基本概念
二、面试题实战
前言
顺序表/ArrayList:
优点:当给定下标的时候,查找速度非常快,适合给定下表的查找,时间复杂度为O(1);
缺点:插入必须要挪动元素,然后才能插入;删除必须挪动元素,才能删除。另外每次扩容也是浪费资源的。因为扩容的时候是1.5倍扩容。比如已经有10个元素了,要放第11个元素,就要扩容到15个,但只有11个元素,就有4个是浪费的。
ArrayList不适合做任意位置的插入和删除比较多的场景。
为了解决这些问题,我们可以使用链式存储:链表。
一、链表的基本概念
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 ,就像火车,一节节的车厢可以看作是节点(结点)。
val:存储数据
next:存储下一个节点的地址(作用是连接当前节点和下一个节点)
🍎注意:
1.链表在逻辑上是连续的,但是在物理上不一定连续。顺序表在物理上一定是连续的,逻辑上是连续的。
2.现实中的结点一般都是从堆上申请出来的。
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。
链式结构分类:通过单\双向、带头\不带头、循环\非循环分为8类
1、单向 带头 循环
2、单向 带头 非循环
3、单向 不带头 循环
4、单向 不带头 非循环
5、双向 带头 循环
6、双向 带头 非循环
7、双向 不带头 循环
8、双向 不带头 非循环
虽然有八种类型,但是我们只学习单向不带头非循环与双向不带头非循环两种类型。
因为笔试面试都是单向不带头非循环结构。而结合类底层是按双向不带头非循环操作的。
链表代码链接
二、面试题实战
1. 删除链表中等于给定值 val 的所有节点。
力扣
class Solution {
public ListNode removeElements(ListNode head, int val) {
//一个结点也没有
if(head==null){
return null;
}
ListNode cur=head;//new一个节点,从头节点开始
ListNode pre=head.next;//new一个节点,比cur先走一步
while(pre!=null){
//当cur节点的值等于val时,要跳过这个节点
if(pre.val==val){
//
cur.next=pre.next;
pre=pre.next;
}else{
//不等于,就继续
cur=pre;
pre=pre.next;
}
}
//当头节点的值等于val时,直接头节点后挪
if(head.val==val){
head=head.next;
}
return head;
}
}
2. 反转一个单链表
力扣
class Solution {
public ListNode reverseList(ListNode head) {
//链表没有节点
if(head==null){
return null;
}
//链表只有一个节点,反转后还是一样的
if(head.next==null){
return head;
}
ListNode cur=head.next;
head.next=null;//将头节点跟第二个节点断开。
//进行头插法
while(cur!=null){
ListNode curNext=cur.next;
cur.next=head;
head=cur;
cur=curNext;
}
return head;
}
}
3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
力扣
class Solution {
public ListNode middleNode(ListNode head) {
//利用快慢指针来做
//new两个新的节点从头节点开始出发,fast每次走两步,slow每次走一步
//如果链表节点为偶数,正好fast.next==null时,slow走到中点。如果是奇数,fast==null时,slow走到中点。
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;//走两步
slow=slow.next;//走一步
}
return slow;
}
}
4. 输入一个链表,输出该链表中倒数第k个结点。
链表中倒数第k个结点_牛客题霸_牛客网
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(k<=0||head==null){
return null;
}
ListNode fast=head;
ListNode slow=head;
//依旧是使用快慢指针的方法
//先new两个节点从头节点开始,fast先向前走k-1步
//然后fast和slow每次同时走一步,当fast到终点的时候,slow正好在倒数第k个节点上。
//当fast.next==null时,正好到终点
while(k-1!=0){
fast=fast.next;
if(fast==null){
return null;
}
k--;
}
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
力扣
class Solution {
public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
ListNode newhead=new ListNode(0);//新new一个虚拟链表
ListNode temp=newhead;//temp指向链表的头节点
while(head1!=null && head2!=null){
if(head1.val>head2.val){//哪个链表的头节点值小,就连哪一个节点
temp.next=head2;
head2=head2.next;
temp=temp.next;
}else{
temp.next=head1;
head1=head1.next;
temp=temp.next;
}
}
//最后一个节点
if(head1!=null){
temp.next=head1;
}
if(head2!=null){
temp.next=head2;
}
return newhead.next;
}
}