深入解析:双向链表LinkedList的实现与应用
·
一,双向链表LinkedList
上一讲说了单向链表,这一讲来说一说双向链表。单向链表为前一个结点的next指向后一个结点,通过后一个结点不能得出前一个结点;而双向链表则多了一个prev,我们可通过prev的调用来指向前一个结点

1. LinkedList实现了List接口
2. LinkedList的底层使用了双向链表
二,LinkedList的使用
1,LinkedList的构造

具体代码实现:
public static void main(String[] args) {
// 构造一个空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new java.util.ArrayList<>();
list2.add("JavaSE");
list2.add("JavaWeb");
list2.add("JavaEE");
// 使用ArrayList构造LinkedList
List<String> list3 = new LinkedList<>(list2);
2,LinkedList的遍历
遍历有三种方法,这里还是提供一段具体代码,供大家理解
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
System.out.println(list);
//for循环遍历
for(int 1=0;i<list.size();i++){
System.out.println(list.get(i)+" ");
}
// foreach遍历
for (int e:list) {
System.out.print(e + " ");
}
System.out.println();
// 使用迭代器遍历---正向遍历
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
System.out.print(it.next()+ " ");
}
System.out.println();
// 使用反向迭代器---反向遍历
ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
System.out.print(rit.previous() +" ");
}
System.out.println();
}
3,LinkedList的其他常用方法介绍

依旧给出部分功能的实现代码
//无头双向链表实现
class MyLinkedLists {
//定义类部类,即每个小结点
static class ListNode{
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
//头插法
public void addFirst(int data){
ListNode cur=new ListNode(data);
if(head==null){
head=last=cur;
}else {
cur.next = head;
head.prev = cur;
head = cur;
}
}
//尾插法
public void addLast(int data){
ListNode cur=new ListNode(data);
if(head==null){
head=last=cur;
}else{
last.next=cur;
cur.prev=last;
last=cur;
}
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
int len=size();
if(index>len||index<0){
return;
}if(index==0){
addFirst(data);
return;
}if(index==len){
addLast(data);
return;
}else {
ListNode code = new ListNode(data);
ListNode cur = isfind(index);//插入结点位置
code.next = cur;
cur.prev.next = code;
cur = code;
code = cur.prev;
}
}
//定义一个私有类来查找目标元素
private ListNode isfind(int index){
ListNode cur=head;
while(index!=0){
cur=cur.next;
index--;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
if(head==null);
while(head!=null){
head=head.next;
if(head.val==key){
return true;
}
}
return false;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
ListNode h=head;
if(h==null)return;
while(h!=null){
if(h.val==key){
//开始删除
if(h==head){
h=h.next;
if(head!=null){
h.prev=null;
}
}else {
h.prev.next=h.next;
if (h.next==null) {
//尾结点
h=h.prev;
}else{
h.next.prev=h.prev;
}
}
return;
}else {
h = h.next;
}
}
}
//删除所有值为key的节点
public void removeAllKey(int key){
ListNode h=head;
if(h==null)return;
while(h!=null){
if(h.val==key){
//开始删除
if(h==head){
head=head.next;//真正要变在head,所以这使用head而不是h
if(head!=null){
head.prev=null;
}
}else {
h.prev.next=h.next;
if (h.next==null) {
//尾结点
h=h.prev;
}else{
h.next.prev=h.prev;
}
}
}
h = h.next;
}
}
//得到单链表的长度
int len=0;
public int size(){
ListNode a=head;
if(a==null)return 0;
while(a!=null){
len++;
a=a.next;
}
return len;
}
public void display(){
while(head!=null){
System.out.print(head.val+" ");
head=head.next;
}
}
public void clear(){
if(head==null)return;
while(head!=null){
head=head.next;
head=null;
}
head=null;
}
}
public class lianbiaoss {
public static void main(String[] args) {
MyLinkedLists mylist=new MyLinkedLists();
mylist.addLast(1);
mylist.addLast(1);
mylist.addLast(1);
mylist.addLast(4);
mylist.addLast(5);
//mylist.addIndex(1,0);
//mylist.remove(5);
mylist.removeAllKey(1);
mylist.display();
//System.out.println(mylist.contains(2));
//System.out.println(mylist.size());
//mylist.clear();
}
}
三,ArrayList和LinkedList的区别

四,相关题目
这里就只给出方法,
1,反转一个单链表
//中心思想,从第二个元素开始每个元素都头插进链表
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null)return head;
ListNode code=head.next;
head.next=null;!!
while(code!=null){
ListNode codeN=code.next;
code.next=head;
head=code;
code=codeN;
}
return head;
}
}
2,返回一个链表的中间值
class Solution {
public ListNode middleNode(ListNode head) {
if(head==null)return head;
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null)不能交换顺序,一口气走两个{
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
}
3,输出倒数第几个元素
class Solution {
public int daoshu(ListNode head, int key) {
if (head == null) return head;
ListNode fast=head;
ListNode slow=head;
int count=0;
while(count!=key-1){
fast=fast.next;
count++;
}fast先走k-,然后slow开始走,fast到最后结点slow到倒数第几个节点
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
4,将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode list3=new ListNode(-1);
ListNode temp=list3;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
temp.next=list1;
list1=list1.next;
}else{
temp.next=list2;
list2=list2.next;
}
temp=temp.next;
}
if(list2==null){
temp.next=list1;
}
if(list1==null){
temp.next=list2;
}
return list3.next;
}
}
5,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
public class Partition {
public ListNode partition(ListNode pHead, int x) {
ListNode as=null;
ListNode ae=null;
ListNode bs=null;
ListNode be=null;
ListNode cur=pHead;
if(cur==null)return cur;
while(cur!=null)
{
if(cur.val<x){
if(as==null){
as=ae=cur;
}else{
ae.next=cur;
ae=ae.next;
}
cur=cur.next;
}else{
if(bs==null){
bs=be=cur;
}else{
be.next=cur;
be=be.next;
}
cur=cur.next;
}
}
if(as==null){
return bs;
}
ae.next=bs;
if(be!=null){
be.next=null;
}
return as;
}
}
6,链表的回文结构
public class PalindromeList {
public boolean chkPalindrome(ListNode head) {
if(head==null)return true;
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}//翻转
ListNode cur=slow.next;
while(cur!=null){
ListNode curN=cur.next;
cur.next=slow;
slow=cur;
cur=curN;
}
//没相遇
while(head!=slow){
if(head.val!=slow.val){
return false;
}//一定是值相同才能true
if(head.next==slow){
return true;
}
head=head.next;
slow=slow.next;
}
return true;
}
}
7,输入两个链表,找出它们的第一个公共结点。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pl=headA;
ListNode ps=headB;
int lenA=0;
int lenB=0;
while(pl!=null){
lenA++;
pl=pl.next;
}
while(ps!=null){
lenB++;
ps=ps.next;
}
//完成两个while pl和ps值为空了
pl=headA;
ps=headB;
int count=lenA-lenB;
if(count<0){
pl=headB;
ps=headA;
count=lenB-lenA;
}
while(count!=0){
pl=pl.next;
count--;
}
//两个引用一直走直到他们相遇
while(pl != ps){
ps=ps.next;
pl=pl.next;
}
return ps;
}
}
8,给定一个链表,判断链表中是否有环
public class Solution {
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(slow==fast){
return true;
}
}
return false;
}
}
9,给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(slow==fast){
break;//如果有环退循环进下一步
}
}
if(fast==null||fast.next==null){
return null;//无环
}
//设到环头节点为x,环长为y,相遇结点到头节点为d,由fast=2slow
//可知当slow从头走,fast从相遇位置走两者到头节点距离一样
//如果环为小环,fast多转几圈一样的
slow=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
五,总结
这段时间没更新也有好好卷,这些代码看起来简单但是要真正清楚中心思想和每一步具体怎么做还是有点难度的,大家可以先看几遍,在去自己敲代码,一定一定一定要自己去敲。双向链表敲不明白的去看上一篇文章,上一篇文章虽然长度不多,但是干货满满,在理解单向链表的基础上会更好实现双向链表。(虽然可以直接调用库函数,但是个人觉得理解每个方法的底层逻辑对提升实力还是很有帮助的,建议敲敲敲敲敲敲敲敲敲敲!!!!!)前几次课上老师给我们看了,腾讯互娱的面经,我的娘诶,吓死人了。一切恐惧来源于卷的力度不够,我要天天敲敲敲。如文章有问题或者不全可联系我哦。如果可以大家动动发财的小手给我点点赞吧
更多推荐
所有评论(0)