什麼是鏈表?
1,鏈表是以節點的方式存儲的,鏈式存儲
2,每個節點包含data域,next域指向下一個節點
3,鏈表的各個節點不一定是鏈式存儲的
4,鏈表分帶頭節點的鏈表和不帶頭結點的鏈表
下面用代碼實現一個單向鏈表
首先定義一個類用來存儲到鏈表中
/**
* 定義HeroNode,每個HeroNode對象就是一個節點
*/
public class HeroNode {
public int no;//編號
public String name;//名字
public String nickName;//暱稱
public HeroNode next;//指向的下一個節點
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
鏈表實現類
/**
* 定義SingleLinkedList管理我們的英雄
*/
public class SingleLinkedList {
//初始化一個頭節點,頭結點不動,不存放任何數據
public HeroNode head = new HeroNode(0, "", "");
/**
* 添加節點到單向鏈表
* 當不考慮編號順序時
* 1,找到當前鏈表的最後節點
* 2,將最後這個節點的next指向新的節點
*
* @param heroNode
*/
public void add(HeroNode heroNode) {
//因爲頭結點不能動,因此我們需要一個輔助遍歷的temp
HeroNode temp = head;
//遍歷鏈表,找到最後的節點
while (true) {
//如果指向下一個鏈表是null 就是最後一個
if (temp.next == null) {
break;
}
//如果不是空 就是還沒到最後一個,將temp後移
temp = temp.next;
}
//當退出while循環時,temp就指向了鏈表的最後
//將最後這個節點的next指向新的節點即可
temp.next = heroNode;
}
//第二種插入方式在添加英雄時,根據排名將英雄輸入到指定位置
public void addByOrder(HeroNode heroNode) {
//使用輔助變量
//我們找的temp是位於添加位置的前一個節點
HeroNode temp = head;
boolean flag = false;//如果要添加的編號已存在flag就變成true
while (true) {
if (temp.next == null) {//說明已經到了鏈表的最後
break;
}
if (temp.next.no > heroNode.no) {//如果temp的下一個節點的編號大於要插入節點的編號,位置找到,在temp的後面插入
break;
}else if(temp.next.no == heroNode.no){//編號相同 已存在
flag = true;
break;
}
//三種條件都不滿足,指針後移
temp = temp.next;
}
if(flag){//已存在 不能添加
System.out.println("編號"+heroNode.no+"已存在");
}else{
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//根據編號修改
public void edit(HeroNode newHeroNode){
HeroNode temp = head.next;//直接指向頭結點的下一個
boolean flag = false;//表示是否找到節點
while(true){
if(temp == null){
break;//表示已經遍歷完鏈表
}
if(temp.no == newHeroNode.no){
//找到
flag = true;
break;
}
temp = temp.next;
}
if(flag){
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else{
System.out.println("沒有找到編號"+newHeroNode.no+"的英雄");
}
}
//刪除節點
public void delete(int no){
//輔助變量
HeroNode temp = head;
boolean flag = false;//標誌是否找到待刪除節點
while(true){
if(temp.next == null){
break;
}
if(temp.next.no == no){
//找到了待刪除節點的前一個節點temp
flag = true;
break;
}
//後移
temp = temp.next;
}
if(flag){
//跳過要刪除的節點,jvm垃圾回收機制會清理沒有作用的節點
temp.next = temp.next.next;
}else{
System.out.println("要刪除的節點不存在");
}
}
//顯示鏈表
public void show() {
//如果鏈表爲空就直接返回
if (head.next == null) {
System.out.println("鏈表爲空");
return;
}
//再次使用一個輔助變量來遍歷
HeroNode temp = head.next;
while (true) {
//判斷鏈表是否到最後
if (temp == null) {
break;
}
//輸出節點信息
System.out.println(temp);
//將temp後移
temp = temp.next;
}
}
}
問題1,統計鏈表中有效節點的個數
public static int getLength(SingleLinkedList link){
if(link.head.next == null){
//空鏈表 返回0
return 0;
}
int length = 0;
HeroNode current = link.head;
//頭結點不是有效節點 不統計頭結點
while(current.next != null){
length += 1;
current = current.next;
}
return length;
}
問題2,獲取鏈表中倒數第k個元素的值
public static HeroNode findLastIndexNode(SingleLinkedList link,int index){
//如果鏈表爲空 直接返回null 沒找到
if(link.head.next == null){
return null;
}
//遍歷獲取鏈表的有效個數,調用剛纔的方法
int size = getLength(link);
//校驗數據是否合法
if(index <= 0 || index > size){
return null;
}
//遍歷size-index的位置,就是倒數第k個節點
HeroNode current = link.head.next;
for(int i = 0;i < size-index;i++){
current = current.next;
}
return current;
}
問題3,實現單鏈表的反轉
public static void reverseList(SingleLinkedList link){
//如果鏈表爲空,直接返回空
if(link.head.next == null){
return ;
}
//定義一個車輔助指針,幫助我們遍歷原來的鏈表
HeroNode current = link.head.next;
HeroNode next = null;//當前節點的下一個節點
//定義一個新的頭結點
HeroNode reverseHead = new HeroNode(0, "", "");
//遍歷原來的鏈表,每遍歷一個節點,就將其取出,並放在新的頭結點的最前端
while (current != null){
next = current.next;//暫時保存當前節點的下一個節點
current.next = reverseHead.next;//將當前節點的下一個節點指向新的連別的最前端
reverseHead.next = current;//再將新的鏈表的最前端指向當前節點
current = next;//當前節點後移
}
//原來的頭結點指向新的頭結點,實現反轉
link.head.next = reverseHead.next;
}
問題4,反向打印單鏈表
對於這個問題,我們可以對鏈表先進行反轉然後再遍歷輸出,但是這樣會破壞原來鏈表的結構,沒必要。
所以採取另外一種方法,利用棧的先進後出的特性,把鏈表中的元素壓入棧中在取出打印。
public static void reversePrint(SingleLinkedList link){
if(link.head.next == null){
return ;
}
Stack<HeroNode> stack = new Stack<>();
HeroNode current = link.head.next;
while(current != null){
stack.push(current);//添加到棧
current = current.next;//後移
}
while (!stack.empty()){
System.out.println(stack.pop());
}
}
雙向鏈表的定義及基本操作
首先定義一個節點類
public class HeroNode2 {
public int no;//編號
public String name;//名字
public String nickName;//暱稱
public HeroNode2 next;//指向當前節點的下一個節點
public HeroNode2 pre;//指向當前節點的前一個節點
public HeroNode2(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
雙向鏈表
public class DoubleLinkedList {
//初始化一個頭節點,頭結點不動,不存放任何數據
public HeroNode2 head = new HeroNode2(0, "", "");
//顯示鏈表
public void show() {
//如果鏈表爲空就直接返回
if (head.next == null) {
System.out.println("鏈表爲空");
return;
}
//再次使用一個輔助變量來遍歷
HeroNode2 temp = head.next;
while (true) {
//判斷鏈表是否到最後
if (temp == null) {
break;
}
//輸出節點信息
System.out.println(temp);
//將temp後移
temp = temp.next;
}
}
//在雙向鏈表的最後添加一個節點
public void add(HeroNode2 heroNode) {
//因爲頭結點不能動,因此我們需要一個輔助遍歷的temp
HeroNode2 temp = head;
//遍歷鏈表,找到最後的節點
while (true) {
//如果指向下一個鏈表是null 就是最後一個
if (temp.next == null) {
break;
}
//如果不是空 就是還沒到最後一個,將temp後移
temp = temp.next;
}
//當退出while循環時,temp就指向了鏈表的最後
temp.next = heroNode;
heroNode.pre = temp;
}
//根據編號修改
public void edit(HeroNode2 newHeroNode){
HeroNode2 temp = head.next;//直接指向頭結點的下一個
boolean flag = false;//表示是否找到節點
while(true){
if(temp == null){
break;//表示已經遍歷完鏈表
}
if(temp.no == newHeroNode.no){
//找到
flag = true;
break;
}
temp = temp.next;
}
if(flag){
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else{
System.out.println("沒有找到編號"+newHeroNode.no+"的英雄");
}
}
//刪除節點
//找到待刪除的節點,自我刪除即可
public void delete(int no){
//輔助變量
HeroNode2 temp = head.next;
boolean flag = false;//標誌是否找到待刪除節點
while(true){
if(temp == null){
break;
}
if(temp.no == no){
//找到了待刪除節點的前一個節點temp
flag = true;
break;
}
//後移
temp = temp.next;
}
if(flag){
temp.pre.next = temp.next;
//如果要刪除的是最後一個節點,就不能執行下面一句操作(待刪除節點後一個節點的pre指向待刪除節點的前一個節點)
if(temp.next != null){
temp.next.pre = temp.pre;
}
}else{
System.out.println("要刪除的節點不存在");
}
}
//按順序添加
public void addByOrder(HeroNode2 node){
//使用輔助變量
//我們找的temp是位於添加位置的前一個節點
HeroNode2 temp = head;
boolean flag = false;//如果要添加的編號已存在flag就變成true
while (true) {
if (temp.next == null) {//說明已經到了鏈表的最後
break;
}
if (temp.next.no > node.no) {//如果temp的下一個節點的編號大於要插入節點的編號,位置找到,在temp的後面插入
break;
}else if(temp.next.no == node.no){//編號相同 已存在
flag = true;
break;
}
//三種條件都不滿足,指針後移
temp = temp.next;
}
if(flag){//已存在 不能添加
System.out.println("編號"+node.no+"已存在");
}else{
node.next = temp.next;
node.pre = temp;
temp.next = node;
temp.next.pre = node;
}
}
}