Java編程內(nèi)功-數(shù)據(jù)結(jié)構(gòu)與算法「單鏈表」
基本介紹
鏈表是有序的列表,但是它在內(nèi)存中存儲(chǔ)如下

- 鏈表是以節(jié)點(diǎn)的方式來(lái)存儲(chǔ).
- 每個(gè)節(jié)點(diǎn)包含data域,next域:指向下一個(gè)節(jié)點(diǎn).
- 如圖:發(fā)現(xiàn)每個(gè)鏈表的各個(gè)節(jié)點(diǎn)不一定是連續(xù)存儲(chǔ)
- 鏈表分帶頭節(jié)點(diǎn)的鏈表和沒(méi)有頭節(jié)點(diǎn)的鏈表,根據(jù)實(shí)際的需求來(lái)確定.
單鏈表介紹
單鏈表(帶頭節(jié)點(diǎn))邏輯結(jié)構(gòu)示意圖如下

單鏈表的應(yīng)用實(shí)例
使用帶head頭的單向鏈表實(shí)現(xiàn)-水滸英雄排行榜管理
- package com.structures.linkedlist;
- public class SingleLinkedListDemo {
- public static void main(String[] args) {
- HeroNode heroNode1 = new HeroNode(1, "宋江", "及時(shí)雨");
- HeroNode heroNode2 = new HeroNode(2, "盧俊義", "玉麒麟");
- HeroNode heroNode3 = new HeroNode(3, "吳用", "智多星");
- HeroNode heroNode4 = new HeroNode(4, "林沖", "豹子頭");
- SingleLinkedList singleLinkedList = new SingleLinkedList();
- singleLinkedList.add(heroNode3);
- singleLinkedList.add(heroNode2);
- singleLinkedList.add(heroNode4);
- singleLinkedList.add(heroNode1);
- singleLinkedList.list();
- }
- }
- //定義SingleLinkedList管理我們的英雄
- class SingleLinkedList {
- //先初始化一個(gè)頭節(jié)點(diǎn),頭節(jié)點(diǎn)不能動(dòng),將來(lái)遍歷用
- private HeroNode head = new HeroNode(0, "", "");
- //添加節(jié)點(diǎn)到單向鏈表
- //思路:當(dāng)不考慮編號(hào)的順序時(shí)
- //1. 找到當(dāng)前鏈表的最后節(jié)點(diǎn)
- //2. 將最后這個(gè)節(jié)點(diǎn)的next域指向新的節(jié)點(diǎn)
- public void add(HeroNode node) {
- //因?yàn)閔ead節(jié)點(diǎn)不能動(dòng),因此我們需要一個(gè)輔助遍歷temp
- HeroNode temp = head;
- //遍歷鏈表,找到最后
- while (temp.next != null) {
- //找到鏈表的最后
- //如果沒(méi)有找到temp就后移
- temp = temp.next;
- }
- temp.next = node;
- }
- //顯示鏈表[遍歷]
- public void list() {
- //判斷鏈表是否為空
- if (head.next == null) {
- System.out.println("鏈表為空");
- }
- //因?yàn)轭^節(jié)點(diǎn)不能動(dòng),因此我們需要一個(gè)輔助變量來(lái)遍歷
- HeroNode temp = head.next;
- while (temp != null) {
- //判斷是否到最后
- //輸出節(jié)點(diǎn)的信息
- System.out.println(temp);
- //將temp后移
- temp = temp.next;
- }
- }
- }
- //定義一個(gè)HeroNode,每個(gè)HeroNode對(duì)象就是一個(gè)節(jié)點(diǎn)
- class HeroNode {
- public int no;
- public String name;
- public String nickName;
- public HeroNode next;//指向下一個(gè)節(jié)點(diǎn)
- //構(gòu)造器
- public HeroNode(int no, String name, String nickName) {
- this.no = no;
- this.name = name;
- this.nickName = nickName;
- }
- public HeroNode getNext() {
- return next;
- }
- public void setNext(HeroNode next) {
- this.next = next;
- }
- @Override
- public String toString() {
- return "HeroNode{" +
- "no=" + no +
- ", name='" + name + '\'' +
- ", nickName='" + nickName + '\'' +
- '}';
- }
- }
- /*
- HeroNode{no=3, name='吳用', nickName='智多星'}
- HeroNode{no=2, name='盧俊義', nickName='玉麒麟'}
- HeroNode{no=4, name='林沖', nickName='豹子頭'}
- HeroNode{no=1, name='宋江', nickName='及時(shí)雨'}
- */
可以看到以上鏈表的實(shí)現(xiàn)方式,在添加英雄時(shí),并未按照英雄的編號(hào)進(jìn)行排序. 下面重新寫(xiě)一個(gè)添加方法,實(shí)現(xiàn)插入英雄時(shí)按編號(hào)排序
- package com.structures.linkedlist;
- public class SingleLinkedListDemo {
- public static void main(String[] args) {
- HeroNode heroNode1 = new HeroNode(1, "宋江", "及時(shí)雨");
- HeroNode heroNode2 = new HeroNode(2, "盧俊義", "玉麒麟");
- HeroNode heroNode3 = new HeroNode(3, "吳用", "智多星");
- HeroNode heroNode4 = new HeroNode(4, "林沖", "豹子頭");
- SingleLinkedList singleLinkedList = new SingleLinkedList();
- singleLinkedList.addByNo(heroNode3);
- singleLinkedList.addByNo(heroNode2);
- singleLinkedList.addByNo(heroNode4);
- singleLinkedList.addByNo(heroNode1);
- singleLinkedList.list();
- }
- }
- //定義SingleLinkedList管理我們的英雄
- class SingleLinkedList {
- //先初始化一個(gè)頭節(jié)點(diǎn),頭節(jié)點(diǎn)不能動(dòng),將來(lái)遍歷用
- private HeroNode head = new HeroNode(0, "", "");
- //添加節(jié)點(diǎn)到單向鏈表
- //思路:當(dāng)不考慮編號(hào)的順序時(shí)
- //1. 找到當(dāng)前鏈表的最后節(jié)點(diǎn)
- //2. 將最后這個(gè)節(jié)點(diǎn)的next域指向新的節(jié)點(diǎn)
- public void add(HeroNode node) {
- //因?yàn)閔ead節(jié)點(diǎn)不能動(dòng),因此我們需要一個(gè)輔助遍歷temp
- HeroNode temp = head;
- //遍歷鏈表,找到最后
- while (temp.next != null) {
- //找到鏈表的最后
- //如果沒(méi)有找到temp就后移
- temp = temp.next;
- }
- temp.next = node;
- }
- //第二種添加英雄的方式,在添加英雄時(shí),根據(jù)排名將英雄插入到指定位置
- //如果有這個(gè)排名,則添加失敗,并給出提示
- public void addByNo(HeroNode heroNode) {
- //因?yàn)閔ead節(jié)點(diǎn)不能動(dòng),因此我們需要一個(gè)輔助遍歷temp
- //因?yàn)閱捂湵?因此找的temp是位于添加位置的前一個(gè)節(jié)點(diǎn),否則加入不了
- HeroNode temp = head;
- boolean flag = false;//標(biāo)識(shí)添加的編號(hào)是否存在,默認(rèn)false
- while (true) {
- if (temp.next == null) {
- break;
- }
- if (temp.next.no > heroNode.no) {//位置找到,就在temp的后面插入
- break;
- } else if (temp.next.no == heroNode.no) {
- //編號(hào)已存在
- flag = true;
- break;
- }
- temp = temp.next;
- }
- if (flag) {
- System.out.printf("準(zhǔn)備插入的英雄的編號(hào) %d 已存在,不能加入\n", heroNode.no);
- } else {
- //插入鏈表temp的后面
- heroNode.next = temp.next;
- temp.next = heroNode;
- }
- }
- //顯示鏈表[遍歷]
- public void list() {
- //判斷鏈表是否為空
- if (head.next == null) {
- System.out.println("鏈表為空");
- }
- //因?yàn)轭^節(jié)點(diǎn)不能動(dòng),因此我們需要一個(gè)輔助變量來(lái)遍歷
- HeroNode temp = head.next;
- while (temp != null) {
- //判斷是否到最后
- //輸出節(jié)點(diǎn)的信息
- System.out.println(temp);
- //將temp后移
- temp = temp.next;
- }
- }
- }
- //定義一個(gè)HeroNode,每個(gè)HeroNode對(duì)象就是一個(gè)節(jié)點(diǎn)
- class HeroNode {
- public int no;
- public String name;
- public String nickName;
- public HeroNode next;//指向下一個(gè)節(jié)點(diǎn)
- //構(gòu)造器
- public HeroNode(int no, String name, String nickName) {
- this.no = no;
- this.name = name;
- this.nickName = nickName;
- }
- public HeroNode getNext() {
- return next;
- }
- public void setNext(HeroNode next) {
- this.next = next;
- }
- @Override
- public String toString() {
- return "HeroNode{" +
- "no=" + no +
- ", name='" + name + '\'' +
- ", nickName='" + nickName + '\'' +
- '}';
- }
- }
- /*
- HeroNode{no=1, name='宋江', nickName='及時(shí)雨'}
- HeroNode{no=2, name='盧俊義', nickName='玉麒麟'}
- HeroNode{no=3, name='吳用', nickName='智多星'}
- HeroNode{no=4, name='林沖', nickName='豹子頭'}
- */
再次進(jìn)行完善功能,添加修改節(jié)點(diǎn)功能
- //修改節(jié)點(diǎn)的信息,根據(jù)no編號(hào)來(lái)修改,即編號(hào)no不能修改.
- public void update(HeroNode heroNode) {
- //判斷是否為空
- if (head.next == null) {
- System.out.println("鏈表為空");
- }
- //找到需要修改的節(jié)點(diǎn),根據(jù)no編號(hào)
- HeroNode temp = head.next;
- boolean flag = false;//表示節(jié)點(diǎn)是否找到
- while (true) {
- if (temp == null) {
- break;
- }
- if (temp.no == heroNode.no) {
- flag = true;
- break;
- }
- temp = temp.next;
- }
- if (flag) {
- temp.name = heroNode.name;
- temp.nickName = heroNode.nickName;
- } else {
- System.out.printf("沒(méi)有找到 編號(hào)%d 的節(jié)點(diǎn),不能修改\n", heroNode.no);
- }
- }
再次進(jìn)行完善功能,添加刪除節(jié)點(diǎn)功能
- //刪除節(jié)點(diǎn)
- public void remove(HeroNode heroNode) {
- //判斷是否為空
- if (head.next == null) {
- System.out.println("鏈表為空");
- }
- HeroNode temp = head.next;
- boolean flag = false;//標(biāo)識(shí)是否找到待刪除的點(diǎn)
- while (true) {
- if (temp == null) {
- break;
- }
- if (temp.next.no == heroNode.no) {
- flag = true;
- break;
- }
- temp = temp.next;
- }
- if (flag) {
- temp.next = temp.next.next;
- } else {
- System.out.printf("無(wú)法刪除 編號(hào)%d 的節(jié)點(diǎn),\n", heroNode.no);
- }
- }
再次進(jìn)行完善功能,添加統(tǒng)計(jì)單鏈表的有效節(jié)點(diǎn)數(shù)功能
- /**
- * 獲取單鏈表的有效節(jié)點(diǎn)數(shù),不統(tǒng)計(jì)頭節(jié)點(diǎn)
- * @param head 鏈表的頭結(jié)點(diǎn)
- * @return 有效節(jié)點(diǎn)數(shù)
- */
- public static int getLength(HeroNode head) {
- if (head.next == null) {
- return 0;
- }
- int count = 0;
- HeroNode temp = head.next;
- while (temp.next != null) {
- count++;
- temp = temp.next;
- }
- return count;
- }
再次進(jìn)行完善功能,添加查找單鏈表中的倒數(shù)第K個(gè)節(jié)點(diǎn)功能
- /**
- * 查找單鏈表的倒數(shù)第K個(gè)節(jié)點(diǎn)[新浪面試題]
- * 思路:
- * 1.先把鏈表從頭到尾遍歷,得到鏈表的總長(zhǎng)度
- * 2.得到size后,從鏈表的第一個(gè)開(kāi)始遍歷到(size-index)個(gè),就可以得到
- *
- * @param head
- * @param index 表示倒數(shù)第index個(gè)節(jié)點(diǎn)
- * @return
- */
- public static HeroNode findLastIndexNode(HeroNode head, int index) {
- if (head.next == null) {
- return null;
- }
- int size = getLength(head);
- if (index <= 0 || index > size) {
- return null;
- }
- HeroNode temp = head.next;
- for (int i = 0; i < (size - index); i++) {
- temp = temp.next;
- }
- return temp;
- }
再次進(jìn)行完善功能,添加單鏈表反轉(zhuǎn)功能
- /**
- * 反轉(zhuǎn)鏈表[騰訊面試題]
- * 思路:
- * 1.先定義一個(gè)reverseHead = new HeroHead();
- * 2.從頭到尾遍歷原來(lái)的鏈表,每遍歷一個(gè)節(jié)點(diǎn),就將其取出,并放在新的鏈表的最前端;
- * 3.原來(lái)的鏈表的head.next = reverseHead.next;
- */
- public static void reverseList(HeroNode head) {
- if (head.next == null || head.next.next == null) {
- return;
- }
- HeroNode curr = head.next;
- HeroNode next = null;//指向當(dāng)前節(jié)點(diǎn)[curr]的下一個(gè)節(jié)點(diǎn)
- HeroNode reverseHead = new HeroNode(0, "", "");
- while (curr != null) {
- next = curr.next;//先暫時(shí)保存curr節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
- curr.next = reverseHead.next;//將curr的下一個(gè)節(jié)點(diǎn)指向新的鏈表的最前端
- reverseHead.next = curr;//將curr連接到新的鏈表上
- curr = next;//讓curr后移
- }
- head.next = reverseHead.next;
- }
再次進(jìn)行完善功能,添加從尾到頭打印單鏈表功能
- /**
- * 使用棧的方式逆序打印[百度面試題]
- */
- public static void reversePrint(HeroNode head) {
- if (head.next == null) {
- return;
- }
- Stack<HeroNode> heroNodes = new Stack<HeroNode>();
- HeroNode temp = head.next;
- while (temp != null) {
- heroNodes.add(temp);
- temp = temp.next;
- }
- while (heroNodes.size() > 0) {
- System.out.println(heroNodes.pop());
- }
- }
【編輯推薦】