Java基礎數據結構–鏈表
文章目錄
鏈表
鏈表的定義:
鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是並不會按線性的順序存儲數據,而是在每一個節點裏存到下一個節點的指針(Pointer)。由於不必須按順序存儲,鏈表在插入的時候可以達到O(1)的複雜度,比另一種線性表順序表快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而順序表相應的時間複雜度分別是O(logn)和O(1)。
使用鏈表結構可以克服數組鏈表需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但是鏈表失去了數組隨機讀取的優點,同時鏈表由於增加了結點的指針域,空間開銷比較大。
在計算機科學中,鏈表作爲一種基礎的數據結構可以用來生成其它類型的數據結構。鏈表通常由一連串節點組成,每個節點包含任意的實例數據(data fields)和一或兩個用來指向上一個/或下一個節點的位置的鏈接
鏈表的作用:
用來存儲邏輯地址連序而物理地址不一定連序的數據存儲結構
單鏈表
如下圖:
鏈表類的寫法思路:
1.寫鏈表類,類裏寫節點類
2.數據域,指針域
3.節點類裏,無參構造函數(爲頭節點準備),有參構造函數(爲增加節點準備)
4.頭引用,鏈表類中,無參構造函數裏面寫生成頭結點對象(main中生成鏈表類對象時,頭結點對象就生成了)
代碼實現:
public class TestLink {
class Node {
int data;
Node next;
public Node() {
this.data = data;
this.next = null;
}
public Node(int val) {
this.data = val;
this.next = null;
}
}
public Node getHead() {
return head;
}
private Node head;
public TestLink() {
this.head = new Node();
}
//此處寫所有與鏈表有關的方法
}
以下爲單鏈表的題:
package pri.qzz.testlink;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.LinkedList;
/**
* @ClassName TestLink
* @Description TODO
* @Author QZZ
* @Date 2018/11/27 12:31
* @Title 願上蒼有好生之德
* @Description:
*
* 1.寫鏈表類,類裏寫節點類
* 2.數據域,指針域
* 3.節點類裏,無參構造函數(爲頭節點準備),有參構造函數(爲增加節點準備)
* 4.頭引用,鏈表類中,無參構造函數裏面寫生成頭結點對象(main中生成鏈表類對象時,頭結點對象就生成了)
*
*
* 1.頭插法,插入數據
* 2.尾插法
* 3.得到單鏈表長度(數據節點個數)
* 4.任意的位置插入
* 5.刪除節點
* 6.刪除值爲val的節點
* 7.打印單鏈表數據
* 8.返回下標
* 9.刪除單鏈表當中所有值爲val的節點 O(n)
* 10.對鏈表進行排序(冒泡)
* 11.找到倒數第K個節點
* 12.刪除倒數第k個節點
* 13.創建一個環
* 14.查找是否有環
* 15.求環的入口點
* 16.求環的長度
* 17.單鏈表的逆置?(使用頭插法逆置)
* 18.單鏈表的反轉
* 19.O(1)時間複雜度
* 20.非遞歸合併兩個有序的遞增的單鏈表
**/
public class TestLink {
class Node {
int data;
Node next;
public Node() {
this.data = data;
this.next = null;
}
public Node(int val) {
this.data = val;
this.next = null;
}
}
public Node getHead() {
return head;
}
private Node head;
public TestLink() {
this.head = new Node();
}
//1.頭插法
public void insertHead(int val) {
Node cur = new Node(val);
cur.next = this.head.next;
this.head.next = cur;
}
//2.尾插法
public void insertTail(int val) {
Node cur = this.head;
while (cur.next != null) {//找到最後一個爲空的節點
cur = cur.next;
}
Node endNode = new Node(val);//生成新節點
cur.next = endNode;//和之前找到的後面爲空的節點鏈接
}
//3.得到單鏈表長度(數據節點個數)
public int getLength() {
Node cur = this.head;
int count = 0;
while (cur.next != null) {
cur = cur.next;
count++;
}
return count;
}
//4.任意的位置插入
public void insertPos(int pos, int val) {
if (pos < 0 || pos > getLength()) {
return;
}
Node cur = this.head;
for (int i = 0; i < pos; i++) {//找到上一個節點
cur = cur.next;
}
Node newNode = new Node(val);
newNode.next = cur.next;
cur.next = newNode;
}
//5.刪除節點(多種寫法,也可以有count代表第幾個節點和pos相等進行刪除)
public void deletePos(int pos) {
if (pos < 0 || pos > getLength()) {
return;
}
Node cur = this.head;
Node deleteCur = this.head.next;
for (int i = 0; i < pos; i++) {
//當前節點的上一個節點
cur = cur.next;
deleteCur = cur.next;
}
cur.next = deleteCur.next;
}
//6.刪除值爲val的節點
public void deleteVal(int val) {
Node cur = this.head.next;
Node pre = this.head;
while (cur != null) {
if (cur.data == val) {
pre.next = cur.next;
}
cur = cur.next;
pre = pre.next;
}
}
//7.打印單鏈表數據
public void print() {
Node cur = this.head;
while (cur.next != null) {
System.out.print(cur.next.data + " ");
cur = cur.next;
}
}
//7.1打印反轉後單鏈表數據 you wen ti 傳入reverHead
public void contraryPrint(Node reverHead) {
Node cur = reverHead;
while (cur.next != null) {
System.out.println(cur.data);
cur = cur.next;
}
}
//8.返回下標
public int findPos(int val) {
int index = 0;
Node cur = this.head.next;
while (cur != null) {
if (cur.data == val) {
index++;
return index;
}
cur = cur.next;
}
return -1;
}
//9.刪除單鏈表當中所有值爲val的節點 O(n)
public void deleteAllVal(int val) {
Node cur = this.head.next;
Node pre = this.head;
while (cur != null) {
if (cur.data == val) {
pre.next = cur.next;
cur = cur.next;
} else {
cur = cur.next;
pre = pre.next;
}
}
}
//10.對鏈表進行排序(冒泡)
public void sortBubble() {
Node cur;
Node runByRun;
int temp;
for (cur = this.head.next; cur.next != null; cur = cur.next) {
for (runByRun = this.head.next; runByRun.next != null; runByRun = runByRun.next) {
if (runByRun.data > runByRun.next.data) {
temp = runByRun.data;
runByRun.data = runByRun.next.data;
runByRun.next.data = temp;
}
}
}
}
//11.找到倒數第K個節點
public int findLastPosVal(int pos) {
if (pos < 0 || pos > getLength()) {
throw new UnsupportedOperationException("Pos不合法");
}
Node cur = this.head;
Node pre = this.head;
while (pos - 1 > 0) {
cur = cur.next;
--pos;
}
while (cur.next != null) {
cur = cur.next;
pre = pre.next;
}
return pre.data;
}
//12.刪除倒數第k個節點
public void deleteLastPos(int pos) {
if (pos < 0 || pos > getLength()) {
throw new UnsupportedOperationException("Pos不合法");
}
Node cur = this.head;
Node pre = this.head;
while (pos > 0) {
cur = cur.next;
--pos;
}
while (cur.next != null) {
cur = cur.next;
pre = pre.next;
}
pre.next = pre.next.next;
}
//13.創建一個環
public void createLoop() {
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = this.head.next.next;
}
//14.查找是否有環()快慢指針
public boolean isLoop() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
//15.求環的入口點
public int getEnterNode() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
break;
}
}
slow = this.head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast.data;
}
//16.求環的長度
public int getLoopLenth() {
Node fast = this.head;
Node slow = this.head;
boolean flg = false;
int len = 0;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow && flg == true) {
break;
}
if (slow == fast && flg == false) {
flg = true;
}
if (flg == true) {
len++;
}
}
return len;
}
//17.單鏈表的逆置?(使用頭插法逆置)
public void reverse() {
Node cur = this.head.next;
Node curNode = null;
this.head.next = null;
while (cur != null) {
curNode = cur.next;
cur.next = this.head.next;
this.head.next = cur;
cur = curNode;
}
}
//18.單鏈表的反轉
public Node contrary() {
Node cur = this.head;
Node curNext;
Node pre = null;
Node reverHead;
while (cur != null) {
curNext = cur.next;
/* if (curNext == null) {
reverHead = cur;
}*/
cur.next = pre;//後面指針域指向前面地址
pre = cur;
cur = curNext;
}
reverHead = pre;
return reverHead;
}
//19.O(1)時間複雜度
public void deleteNode (Node nodeStart, Node nodeDelete) {
if (nodeDelete.next != null) {//如果刪除的不是尾節點
Node nodeDeleteNext = nodeDelete.next;
nodeDelete.data = nodeDeleteNext.data;
nodeDelete.next = nodeDeleteNext.next;
nodeDeleteNext = null;
}else if (nodeStart == nodeDelete) {
nodeStart = null;
nodeDelete = null;
}else {//尾節點
//尾節點的前驅
Node cur = nodeStart;
while (cur.next != nodeDelete) {
cur = cur.next;
}
//cur ==> 尾節點的前驅
cur.next = null;
nodeDelete = null;
}
return;
}
public static boolean isCut(TestLink testLink1,TestLink testLink2) {
TestLink.Node head1 = testLink1.getHead();//指向長的單鏈表
TestLink.Node head2 = testLink2.getHead();//指向短的單鏈表
int len1 = testLink1.getLength();
int len2 = testLink2.getLength();
int myLen = len1-len2;
if(myLen < 0) {
head1 = testLink2.getHead();
head2 = testLink1.getHead();
myLen = len2-len1;
}
//head1===>指向長的單鏈表
for (int i = 0; i < myLen; i++) {
head1 = head1.next;
}
while(head1 != head2 && head1 != null && head2 != null) {
head1 = head1.next;
head2 = head2.next;
}
if(head1 == head2 && head1 != null && head2 != null) {
return true;
}
return false;
}
//20.非遞歸合併兩個有序的遞增的單鏈表
public static TestLink.Node mergeLink(TestLink link1,TestLink link2){
TestLink.Node newHead = null;//返回的頭結點
/*TestLink.Entry p1 = link1.getHead().next;
TestLink.Entry p2 = link2.getHead().next;*/
TestLink.Node p1 = link1.getHead();
TestLink.Node p2 = link2.getHead();
if(p1.next.data < p2.next.data) {
//newHead = link1.getHead();
newHead = p1;
} else {
//newHead = link2.getHead();
newHead = p2;
}
p1 = p1.next;
p2 = p2.next;
TestLink.Node tmpHead = newHead;
while(p1 != null && p2 != null) {
if(p1.data < p2.data) {
tmpHead.next = p1;
p1 = p1.next;
} else {
tmpHead.next = p2;
p2 = p2.next;
}
tmpHead = tmpHead.next;
}
if(p1 == null) {
tmpHead.next = p2;
}
if(p2 == null) {
tmpHead.next = p1;
}
return newHead;
}
//打印合並後的單鏈表
public static void showMerge(TestLink.Node entry){
TestLink.Node cur = entry.next;
while(cur != null){
System.out.print(cur.data + " ");
cur = cur.next;
}
}
}
單鏈表中一些題的圖解:
1.非遞歸合併兩個有序遞增的單鏈表
2.單鏈表的反轉
循環鏈表
(上面有循環鏈表圖)
代碼以及思想:
package pri.qzz.testlink;
/**
* @ClassName TestClinkDemo
* @Description TODO
* @Author QZZ
* @Date 2018/12/5 17:08
* @Title 願上蒼有好生之德
* @Description:
* 循環鏈表(主要關鍵在
*
* 1、public Clink() {} 中生成了頭結點,頭結點的next和自己鏈在一起)
* 2、以前while() 中不等於null,null換成this.next
* 3、尾插最後的節點要和頭結點鏈接起來(頭插法無所謂和一般鏈表寫法一樣)
**/
class Clink {
class Node {
private int data;
private Node next;
public Node() {
this.data = -1;
this.next = null;
}
public Node(int val) {
this.data = val;
this.next = null;
}
}
public Node head;
public Clink() {
this.head = new Node();
this.head.next = this.head;
}
//頭插法
public void insertHead(int val) {
Node cur = new Node(val);
cur.next = this.head.next;
this.head.next = cur;
}
//尾插法
public void insertTail(int val) {
Node cur = this.head;
while (cur.next != this.head) {
cur = cur.next;
}
Node newCur = new Node(val);
cur.next = newCur;
newCur.next = this.head;
}
//刪除所有值爲val的節點
public void deleteAllVal(int val) {
Node cur = this.head.next;
Node pre = this.head;
while (cur != this.head) {
if (cur.data == val) {
pre.next = cur.next;
cur = pre.next;
}else {
pre = cur;
cur = cur.next;
}
}
}
public void show() {
Node cur = this.head.next;
while (cur != this.head) {
System.out.println(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
}
public class TestClinkDemo {
public static void main(String[] args) {
Clink Node = new Clink();
//entry.insertHead(12);
Node.insertTail(1);
Node.insertTail(1);
Node.insertTail(2);
Node.insertTail(1);
Node.insertTail(3);
Node.show();
Node.deleteAllVal(1);
Node.show();
}
}
雙向鏈表
如下圖:
代碼以及思想:
class Dlink {
class Node {
private int data;
private Node next;
private Node pre;
public Node () {
this.data = -1;
this.pre = null;
this.next = null;
}
public Node (int val) {
this.data = val;
this.pre = null;
this.next = null;
}
}
private Node head;
public Dlink () {
this.head = new Node();
}
//頭插法
public void insertHead (int val) {
Node cur = new Node(val);
cur.next = this.head.next;
cur.pre = this.head;
this.head.next = cur;
if (cur.next != null) {
cur.next.pre = cur;
}
}
//尾插法
public void insertTail (int val) {
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
Node newCur = new Node(val);
cur.next = newCur;
newCur.pre = cur;
}
//刪除所有值爲val的節點
public void deleteAllVal (int val) {
Node cur = this.head.next;
while (cur != null) {
if (cur.data == val) {
cur.pre.next = cur.next;
if(cur.next != null) {
cur.next.pre = cur.pre;
}
}
cur = cur.next;
}
}
//打印
public void show () {
Node cur = this.head.next;
while (cur != null) {
System.out.println(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
}
public class TestDlinkDemo {
public static void main(String[] args) {
Dlink dlink = new Dlink();
/*dlink.insertHead(1);
dlink.insertHead(2);
dlink.insertHead(3);
dlink.insertHead(4);
dlink.insertHead(5);*/
dlink.insertTail(1);
dlink.insertTail(1);
dlink.insertTail(3);
dlink.insertTail(4);
dlink.insertTail(1);
dlink.show();
dlink.deleteAllVal(1);
dlink.show();
}
}
Github 代碼鏈接:
https://github.com/QzzBL/Qzz_Java/tree/master/Java基礎數據結構/鏈表
2018.12.08/週六
by 922