一、链表
1.1 链表的概念
链表(Linked List)是一种常见的数据结构,用于存储和组织数据。它由一系列节点(Node)组成,每个节点包含两个主要部分:数据域(Data)和指针域(Pointer)。
数据域存储节点所需的数据或信息,可以是任意类型的数据,如整数、字符、对象等。指针域则指向链表中的下一个节点,将节点连接起来形成链表结构。
链表中的节点并不一定按照物理上的连续位置存储,而是通过指针域相互连接。这使得链表能够灵活地插入、删除和修改节点,而无需像数组那样进行元素的移动。
链表的优点是可以高效地插入和删除节点,而无需移动其他节点。然而,由于链表中的节点不是连续存储的,访问特定位置的节点需要从头部开始遍历,因此随机访问的效率较低。
1.2 链表的结构
在实际的应用场景中,链表的结构非常多样,以下是常见的链表结构:
- 单向链表
单向链表(Singly Linked List)是一种基本的链表结构,每个节点包含两个主要部分:数据域和指针域。以下是单向链表的结构示意图:
在单向链表中,每个节点包含一个数据域,用于存储节点所需的数据,以及一个指针域,指向链表中的下一个节点。最后一个节点的指针域通常指向空值(NULL),表示链表的结束。
通过这种方式,节点之间通过指针链接在一起,形成一个链表结构。链表的头部节点通过外部引用进行访问,然后可以依次遍历访问链表中的每个节点。
由于单向链表只有一个方向的指针,所以只能从头部节点开始顺序遍历访问,无法直接访问后续节点,也无法在常量时间内进行反向遍历。
单向链表在插入和删除节点时具有较好的性能,因为只需要修改节点的指针,而不需要移动其他节点。然而,在访问特定位置的节点时,需要从头部节点开始遍历整个链表,导致访问效率较低。
2.双向链表
双向链表(Doubly Linked List)的每个节点包含两个指针,分别指向前一个节点和后一个节点。这使得节点可以在两个方向上进行遍历和访问,提供了更多的灵活性和功能。
在双向链表中,每个节点包含一个数据域和两个指针域。除了指向下一个节点的指针域(Next Pointer),还有一个指向前一个节点的指针域(Prev Pointer)。头部节点的 Prev Pointer 通常指向空值(NULL),尾部节点的 Next Pointer 也指向空值。
通过 Prev Pointer 和 Next Pointer,双向链表允许在链表的两个方向上遍历和访问节点。这使得在某些情况下,例如在链表末尾插入或删除节点,可以更高效地操作链表。
与单向链表相比,双向链表的操作稍微复杂一些,因为每个节点需要维护两个指针。但双向链表提供了更多的功能和灵活性,如可以从头部或尾部快速插入或删除节点,以及可以在两个方向上进行遍历和搜索。
3.带头节点的链表
带头结点的链表在头部添加了一个额外的节点作为头结点(Header Node)或哑节点(Dummy Node),这个头结点不存储任何实际的数据。
头结点位于链表的起始位置,其主要作用是方便链表的操作和管理。它不存储实际的数据,只包含一个指针域,指向链表中的第一个实际节点。
带头结点的链表的优点在于简化了链表的操作。它确保链表中始终存在一个非空节点,简化了插入、删除和遍历等操作的实现。此外,头结点可以用于存储链表的一些统计信息或提供其他便利的功能。
在带头结点的链表中,链表的第一个实际节点为头结点的下一个节点。通过头结点,可以轻松地访问链表中的实际数据节点,并进行相应的操作。
当遍历带头结点的链表时,通常从头结点的下一个节点开始,直到遇到指针域为 NULL 的节点,表示链表结束。
4.循环链表
循环链表(Circular Linked List)是一种特殊的链表结构,其中最后一个节点的指针指向链表的头部节点,形成一个闭环。
在循环链表中,每个节点仍然包含数据域和指针域,但最后一个节点的指针域不再指向空值(NULL),而是指向链表的头部节点。
这样的设计使得链表形成一个循环结构,可以通过任何节点开始遍历整个链表。从任何节点出发,通过指针的循环跳转,可以访问链表中的所有节点。
循环链表的优点在于在遍历和操作链表时,不需要考虑链表的结束位置。无论从哪个节点开始遍历,总能回到起始位置,避免了在常规链表中遇到的空指针异常。
二、LinkedList的简介
在Java中,LinkedList(链表)是Java集合框架提供的一个实现了List接口的类。它是基于双向链表结构实现的,可以用于存储和操作元素的有序集合。
三、模拟实现链表
1.display方法
思路:从头结点开始遍历,若该结点不等于null就打印输出该结点的元素。
问题:
- 怎么从一个结点走到下一个结点?:head = head.next;
- 怎么判断所有结点都已经走完了?:head= null;
public void display() {
ListNode cur = head;
int count = 0;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
count++;
}
System.out.println();
System.out.println(count);
}
【解释】:为什么创建一个cur代替head:如果直接用head遍历那么当打印输出结束后head就为null,相当于链表就空了,因此用cur代替head。
2.size方法
思路:定义一个计数器,从头开始遍历链表,每走一个结点计数器加一,最后返回计数器结果。
public int size() {
ListNode cur = head;
int count = 0;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
3.contains方法
思路:遍历链表,每走一步判断结点的元素是否等于key,等于则直接返回true,若遍历完成仍没有则返回false
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
4.addFirst方法
思路:
- 创建一个结点
- 改变插入结点的next域
- 改变head
public void addFirst(int data) {
ListNode node = new ListNode(data);
node.next = head;
head = node;
}
5.addLast方法
思路:
- 实例化一个结点
- 找到最后一个结点cur
- cur.next = node;
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
return;
}
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
6.addIndex方法
public void addIndex(int index, int val) {
if (index < 0 || index > size()) {
return;
}
if (index == 0) {
addFirst(val);
return;
}
if (index == size()) {
addLast(val);
return;
}
ListNode cur = findIndex(index);
ListNode node = new ListNode(val);
node.next = cur.next;
cur.next = node;
}
7.remove方法
public void remove(int k) {
if (head == null)
return;
if (head.val == k) {
head = head.next;
return;
}
ListNode cur = finKey(k);
if (cur == null) {
return;//没有要删除的数据
}
ListNode del = cur.next;
cur.next = del.next;
}
8.removeAllKey方法
public void removeAllKey(int key) {
if (head == null) return;
ListNode cur = head.next;
ListNode prev = head;
while (cur != null) {
if (cur.val == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (head.val == key) {
head = head.next;
}
}
9.clear方法
public void clear() {
ListNode cur = head;
while (cur != null) {
ListNode curN = cur.next;
cur.next = null;
cur = curN;
}
head = null;
}
四、双向链表的实现
package demolinkedlist;
public class MyLinkedList {
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 node=new ListNode(data);
if (head==null){
head=node;
last=node;
} else {
node.next=head;
head.prev=node;
node=head;
}
}
public void addLast(int data){
ListNode node=new ListNode(data);
if (head==null){
head=last=node;
}else {
last.next=node;
node.prev=last;
last=node;
}
}
public void addIndex(int index,int data){
if (index<0||index>size()){
return;
}
if (index==0){
addFirst(data);
}
if (index==size()){
addLast(data);
}
ListNode cur=searchIndex(index);
ListNode node=new ListNode(data);
node.next=cur;
cur.prev.next=node;
cur.prev=node.prev;
cur.prev=node;
}
private ListNode searchIndex(int index){
ListNode cur=head;
int count=0;
while (count!=index){
cur=cur.next;
count++;
}
return cur;
}
public void remove(int key){
ListNode cur=head;
while (cur!=null){
if (cur.val==key){
if(cur==head){
head=head.next;
if (head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if (cur.next==null){
cur.prev.next=cur.next;
last=cur.prev;
}else {
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}
}
return;
}else {
cur=cur.next;
}
}
}
public boolean contains(int key){
ListNode cur=head;
while (cur!=null){
if(cur.val==key){
return true;
}
cur=cur.next;
}
return false;
}
public int size(){
ListNode cur=head;
int count=0;
while (cur!=null){
count++;
cur=cur.next;
}
return count;
}
public void display(){
ListNode cur=head;
while (cur!=null){
System.out.print(cur.val+" ");
cur=cur.next;
}
}
public void removeAllKey(int key){
ListNode cur=head;
while (cur!=null){
if (cur.val==key){
if(cur==head){
head=head.next;
if (head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if (cur.next==null){
cur.prev.next=cur.next;
last=cur.prev;
}else {
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}
}
}
cur=cur.next;
}
}
public void clear(){
ListNode cur=head;
ListNode curN=head.next;
while (cur!=null){
cur.next=null;
curN.prev=null;
cur=curN;
curN=curN.next;
}
head=null;
last=null;
}
}
双链表与单链表相似!!