著者ホームページ:ペーパージーのブログ
この記事の著者: 皆さんこんにちは、paper jie です。この記事を読んでいただきありがとうございます。Yijiansanlian へようこそ。
この記事は「JAVA データ構造」コラムに収録されており、大学生やプログラミング初心者向けに丁寧に作成されています。著者は、JavaSE の基本的な知識をすべて網羅するために、多大な費用 (時間とエネルギー) を費やして構築しました。
その他コラム:「アルゴリズムの詳細解説」「C言語」「javaSE」など
コンテンツ共有: この号では、データ構造におけるリンク リストの知識を共有します。
目次
リンクされたリスト
リンクリストの概念と構造
リンク リストは物理的に不連続なストレージ構造であり、データ要素の論理的順序はリンク リスト内の参照リンク順序によって実現されます。本物の緑色の電車と考えることができます
ここで注意してください:
チェーンは論理的には連続していますが、必ずしも物理的に連続しているとは限りません。
実際には、ノードは通常、ヒープから適用されます。
ヒープから要求された領域は特定の戦略に従って割り当てられるため、2 回適用される領域は連続的である場合もあれば、不連続である場合もあります。
連結リストの構造は多様であり、状況に応じて使い分けることができますが、一般的には次のような構造が使用されます。
一方向または双方向
主導権を握ることと主導権を奪わないこと
周期的と非周期的
これらの構造のうち、次の 2 つのタイプに注目する必要があります。
ヘッドレス一方向非巡回リンク リスト:構造は単純で、一般にデータを単独で保存することはありません。実際、ハッシュ バケットやグラフの隣接リストなど、他のデータ構造の下部構造として使用されることが多くなります。
ヘッドレス双方向リンク リスト: Java コレクション フレームワークでは、LinkedList の下位実装はヘッドレス双方向循環リンク リストです。
一方向リンクリストのシミュレーション実装
以下は、一方向リンク リストで実装する必要があるいくつかの基本関数です。
// 1、无头单向非循环链表实现
public class SingleLinkedList {
//头插法
public void addFirst(int data){
}
//尾插法
public void addLast(int data){
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
return false;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
}
//删除所有值为key的节点
public void removeAllKey(int key){
}
//得到单链表的长度
public int size(){
return -1;
}
public void clear() {
}
public void display() {}
}
特定の実装コード
私のリンクリスト
package myLinkedList;
import sun.awt.image.ImageWatched;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2023-09-14
* Time: 10:38
*/
public class MyLinkedList implements IList{
static class LinkNode {
public int value;
public LinkNode next;
public LinkNode(int data) {
this.value = data;
}
}
LinkNode head;
public void createNode() {
LinkNode linkNode1 = new LinkNode(12);
LinkNode linkNode2 = new LinkNode(23);
LinkNode linkNode3 = new LinkNode(34);
LinkNode linkNode4 = new LinkNode(56);
LinkNode linkNode5 = new LinkNode(78);
linkNode1.next = linkNode2;
linkNode2.next = linkNode3;
linkNode3.next = linkNode4;
linkNode4.next = linkNode5;
this.head = linkNode1;
}
@Override
public void addFirst(int data) {
//实例化一个节点
LinkNode firstNode = new LinkNode(data);
if(this.head == null) {
this.head = firstNode;
return;
}
//将原第一个对象的地址给新节点的next,也就是将head给新next
firstNode.next = this.head;
//将新的对象的地址给head头
this.head = firstNode;
}
@Override
public void addLast(int data) {
//实例化一个节点
LinkNode lastNode = new LinkNode(data);
//找到最后一个节点
LinkNode cur = this.head;
while(cur.next!= null) {
cur = cur.next;
}
cur.next = lastNode;
//将最后一个节点的next记录插入节点的地址
}
@Override
public void addIndex(int index, int data) throws indexillgality {
if(index < 0 || index > size()) {
throw new indexillgality("index不合法");
}
LinkNode linkNode = new LinkNode(data);
if(this.head == null) {
addFirst(data);
return;
}
if(size() == index ) {
addLast(data);
return;
}
LinkNode cur = this.head;
int count = 0;
while(count != index - 1) {
cur = cur.next;
count++;
}
linkNode.next = cur.next;
cur.next = linkNode;
}
@Override
public boolean contains(int key) {
LinkNode cur = this.head;
while(cur != null) {
if(cur.value == key) {
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
if(this.head.value == key) {
this.head = this.head.next;
return ;
}
//找前驱
LinkNode cur = findprev(key);
//判断返回值
if(cur != null) {
//删除
LinkNode del = cur.next;
cur.next = del.next;
//cur.next = cur.next.next;
}
}
//找删除的前驱
private LinkNode findprev(int key) {
LinkNode cur = head;
while(cur.next != null) {
if(cur.next.value == key) {
return cur;
}
cur = cur.next;
}
return null;
}
@Override
public void removeAllKey(int key) {
if(size() == 0) {
return ;
}
if(head.value == key) {
head = head.next;
}
LinkNode cur = head.next;
LinkNode prev = head;
while(cur != null) {
if(cur.value == key) {
prev.next = cur.next;
}
prev = cur;
cur = cur.next;
}
}
@Override
public int size() {
LinkNode cur = head;
int count = 0;
while(cur != null) {
count++;
cur = cur.next;
}
return count;
}
@Override
public void display() {
LinkNode x = head;
while(x != null) {
System.out.print(x.value + " ");
x = x.next;
}
System.out.println();
}
@Override
public void clear() {
LinkNode cur = head;
while(cur != null) {
LinkNode curNext = cur.next;
cur.next = null;
cur = curNext;
}
head = null;
}
}
インデックス性
現時点ではカスタム例外
package myLinkedList;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2023-09-14
* Time: 12:55
*/
public class indexillgality extends RuntimeException {
public indexillgality(String message) {
super(message);
}
}
リンクリスト
LinkedListのシミュレーション実装
これは、ヘッドレス二重リンク リストの実装と同等であり、必要な基本機能は次のとおりです。
// 2、无头双向链表实现
public class MyLinkedList {
//头插法
public void addFirst(int data){ }
//尾插法
public void addLast(int data){}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){}
//删除第一次出现关键字为key的节点
public void remove(int key){}
//删除所有值为key的节点
public void removeAllKey(int key){}
//得到单链表的长度
public int size(){}
public void display(){}
public void clear(){}
}
私のリンクリスト
package myLinkedList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2023-09-20
* Time: 18:49
*/
public class MyLinkedList implements IList {
//单个节点
public static class ListNode {
private int val;
private ListNode prev;
private ListNode next;
public ListNode(int val) {
this.val = val;
}
}
ListNode head;
ListNode last;
@Override
public void addFirst(int data) {
ListNode cur = new ListNode(data);
if(head == null) {
cur.next = head;
head = cur;
last = cur;
}else {
cur.next = head;
head.prev = cur;
head = cur;
}
}
@Override
public void addLast(int data) {
ListNode cur = new ListNode(data);
if(head == null) {
head = cur;
last = cur;
} else {
last.next = cur;
cur.prev = last;
last = cur;
}
}
@Override
public void addIndex(int index, int data) throws Indexexception {
ListNode cur = new ListNode(data);
if(index < 0 || index > size()) {
throw new Indexexception("下标越界");
}
//数组为空时
if(head == null) {
head = cur;
last = cur;
return ;
}
//数组只有一个节点的时候
if(head.next == null || index == 0) {
head.prev = cur;
cur.next = head;
head = cur;
return;
}
if(index == size()) {
last.next = cur;
cur.prev = last;
return ;
}
//找到对应下标的节点
ListNode x = head;
while(index != 0) {
x = x.next;
index--;
}
//头插法
cur.next = x;
cur.prev = x.prev;
x.prev.next = cur;
x.prev = cur;
}
@Override
public boolean contains(int key) {
ListNode cur = head;
while(cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
if(head == null) {
return;
}
ListNode cur = head;
while(cur != null) {
if(cur.val == key) {
if(cur.next == null && cur.prev == null) {
head = null;
last = null;
return;
}else if(cur.next == null){
cur.prev.next = null;
last = cur.prev;
return;
}else if(cur.prev == null) {
head = cur.next;
cur.next.prev = null;
return ;
}else {
ListNode frone = cur.prev;
ListNode curnext = cur.next;
frone.next = curnext;
curnext.prev = frone;
return ;
}
}
cur = cur.next;
}
}
@Override
public void removeAllKey(int key) {
if(head == null) {
return;
}
ListNode cur = head;
while(cur != null) {
if(cur.val == key) {
if(cur.next == null && cur.prev == null) {
head = null;
last = null;
} else if(cur.next == null){
cur.prev.next = null;
last = cur.prev;
}else if(cur.prev == null) {
head = cur.next;
cur.next.prev = null;
}else {
ListNode frone = cur.prev;
ListNode curnext = cur.next;
frone.next = curnext;
curnext.prev = frone;
}
}
cur = cur.next;
}
}
@Override
public int size() {
int count = 0;
ListNode cur = head;
while(cur != null) {
count++;
cur = cur.next;
}
return count;
}
@Override
public void display() {
ListNode cur = head;
while(cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
@Override
public void clear() {
if(head == null) {
return;
}
ListNode cur = head.next;
while(cur != null) {
head = null;
head = cur;
cur = cur.next;
}
head = null;
}
}
インデックス例外
これもカスタム例外です
package myLinkedList;
/**
* Created with IntelliJ IDEA.
* Description:
* User: sun杰
* Date: 2023-09-21
* Time: 9:47
*/
public class Indexexception extends RuntimeException{
public Indexexception(String message) {
super(message);
}
}
JavaのLinkedList
LinkedList の最下層は二重リンクリスト構造となっており、リンクリストは連続した空間に要素を格納しないため、要素は別々のノードに格納され、ノード間は参照によって接続されます。このため、任意の位置に要素を挿入したり削除したりする際に、要素を移動する必要がなくなり効率的になります。
コレクション フレームワークでは、LinkedList は List インターフェイスも実装します。
知らせ:
LinkedList は List インターフェイスを実装します
LinkedList の基礎となる層は二重リンク リストを使用します
Linked は RandomAccess インターフェイスを実装していないため、LinkedList はランダム アクセスをサポートしません。
LinkedList は、ランダムな位置で要素を挿入および削除する場合に効率が高く、複雑さは O(1) です。
LinkedList は、任意の位置に挿入するのに適しています。
LinkedListの使用
LinkedList の構築:
一般的に、次の 2 つの方法があります。
引数のない構造:
List<Integer> list = new LinkedList<>();
他のコレクション コンテナーの要素を使用してリストを構築します。
public LinkedList(Collection<? extends E> c)
栗:
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);
}
LinkedList の基本メソッド:
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1); // add(elem): 表示尾插
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
System.out.println(list.size());
System.out.println(list);
// 在起始位置插入0
list.add(0, 0); // add(index, elem): 在index位置插入元素elem
System.out.println(list);
list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
list.removeFirst(); // removeFirst(): 删除第一个元素
list.removeLast(); // removeLast(): 删除最后元素
list.remove(1); // remove(index): 删除index位置的元素
System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
if(!list.contains(1)){
list.add(0, 1);
}
list.add(1);
System.out.println(list);
System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
int elem = list.get(0); // get(index): 获取指定位置元素
list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
System.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
List<Integer> copy = list.subList(0, 3);
System.out.println(list);
System.out.println(copy);
list.clear(); // 将list中元素清空
System.out.println(list.size());
}
LinkedList のさまざまな走査
対象:
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
list.add(1);
list.add(3);
list.add(5);
list.add(2);
list.remove(1);
for (int x:list) {
System.out.print(x + " ");
}
}
イテレータを使用してトラバースします。
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()) {
System.out.println(it.next() + " ");
}
}