使用带head头的单向链表实现–水浒英雄排行榜管理
package com.atguigu.linkedlist;
public class SingleLinkedListDemo {
public static void main(String[] args) {
//创建几个节点
HeroNode hero1 = new HeroNode(1, "松江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
SingleLinkedList linkedList = new SingleLinkedList();
linkedList.add(hero1);
linkedList.add(hero2);
linkedList.add(hero3);
linkedList.add(hero4);
linkedList.list();
}
}
//定义SingleLinkedList
class SingleLinkedList {
//定义头节点
private HeroNode head = new HeroNode(0, "", "");
//添加节点到单向链表
public void add(HeroNode heroNode) {
//保存头节点
HeroNode temp = head;
//遍历链表,找到最后一个节点
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
//循环跳出时,temp指向最后一个节点,然后将新节点添加即可
temp.next = heroNode;
}
//显示链表
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
while (true) {
//判断是否到链表最后
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
//定义HeroNode
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 + '\'' +
'}';
}
}
//根据排名进行添加
public void add2(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
//如果一直找到最后一个位置,直接添加即可
if (temp.next == null) {
temp.next = heroNode;
break;
}
//当前节点已存在
if (temp.no == heroNode.no) {
flag = true;
break;
}
//找到了位置
if (temp.no < heroNode.no && temp.next.no > heroNode.no) {
heroNode.next = temp.next;
temp.next = heroNode;
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("当前节点已存在,添加失败");
}
}
public void add3(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no > heroNode.no) {
break;
} else if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("已经存在,不可添加");
}else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
public void update(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
heroNode.next = temp.next.next;
temp.next = heroNode;
} else {
System.out.println("未找到目标节点,更新失败");
}
}
public void delete(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.println("未找到目标节点,删除失败");
}
}
在对单链表进行操作时,很重要的一点是,要确认你要找的元素时哪一个元素
比如:你想在某个位置插入一个元素,或者是删除某一位置的元素,此时你要找的是这个位置元素的 前一个 元素。
而在后面—合并两个有序的单链表,合并之后的链表依旧有序—这个试题中
是要遍历每一个节点,此时,应该直接找到你要遍历的那个节点
求单链表中节点的个数
/**
* 获取单链表节点的个数(有头节点的,不统计头节点)
* @param head 头节点
* @return 有效节点个数
*/
public static int getLength(HeroNode head) {
int length = 0;
while (true) {
if (head.next == null) {
break;
}
length++;
head = head.next;
}
return length;
}
查找单链表中倒数第k个节点
/**
* 查找单链表中倒数第k个节点
* @param k
* @return 倒数第K个节点
*/
public HeroNode getK(int k) {
int length = getLength(head);
int k1 = length - k + 1;
if (k1 <= 0) {
System.out.println("没有倒数第" + k + "个节点");
return null;
}
HeroNode temp = head;
while (k1 != 0) {
temp = temp.next;
k1--;
}
return temp;
}
单链表的反转
思路:
/**
* 单链表的反转
* @param list 原始单链表
* @return 反转后的单链表
*/
public static SingleLinkedList reverse(SingleLinkedList list) {
int length = getLength(list.head);
if (length < 2) {
System.out.println("链表长度小于2,无需进行反转");
return list;
}
SingleLinkedList newList = new SingleLinkedList();
HeroNode reverseNode;
//这里只要遍历list即可~~懒得改了
while ((reverseNode = list.deleteNode(1)) != null) {
newList.addNode(reverseNode, 1);
}
return newList;
}
/**
* 删除第i个节点
* @param i
* @return 第i个节点
*/
public HeroNode deleteNode(int i) {
if (i > getLength(head)) {
System.out.println("没有第" + i + "个节点");
return null;
}
//找i前面的节点
i = i - 1;
HeroNode temp = head;
while (i != 0) {
if (temp.next == null) {
break;
}
temp = temp.next;
i--;
}
//将第i个节点存储,作为返回值
HeroNode result = temp.next;
//删除
temp.next = result.next;
return result;
}
/**
* 将 Node 添加到第i个位置
* @param node
* @param i
*/
public void addNode(HeroNode node, int i) {
if (i > getLength(head) + 1) {
System.out.println("无法将当前节点加入链表,因为位置超出链表长度");
return;
}
//找i前面一个位置
i = i - 1;
HeroNode temp = head;
while (i != 0) {
if (temp.next == null) {
break;
}
temp = temp.next;
i--;
}
//判断是否已经找到
if (i == 0) {
node.next = temp.next;
temp.next = node;
}
}
/**
* 获取单链表节点的个数(有头节点的,不统计头节点)
* @param head 头节点
* @return 有效节点个数
*/
public static int getLength(HeroNode head) {
int length = 0;
while (true) {
if (head.next == null) {
break;
}
length++;
head = head.next;
}
return length;
}
从尾到头打印单链表
利用栈先进后出的特点,实现逆序打印
/**
* 使用stack进行逆序打印
* @param head
*/
public static void reversePrint(HeroNode head) {
//链表空,不打印
if (head.next == null) {
return;
}
Stack<HeroNode> stack = new Stack<>();
while (head.next != null) {
stack.push(head.next);
head = head.next;
}
while (stack.size() > 0) {
System.out.println(stack.pop());
}
}
合并两个有序的单链表,合并之后的链表依旧有序
合并链表的时候注意不要用头节点去next,注意引用同一个对象时,操作的是同一块内存,防止在生成新的链表的时候,h1,h2被改变
/**
* 合并两个链表
* @param head1
* @param head2
* @return 合并后的新链表
*/
public static SingleLinkedList merge(HeroNode head1, HeroNode head2) {
if (head1.next == null || head2.next == null) {
return null;
}
SingleLinkedList singleLinkedList = new SingleLinkedList();
//这里非常有必要让head比h1,h2慢一步
//否则在修改head的时候,h1,h2会跟着改变
HeroNode head = singleLinkedList.getHead();
HeroNode h1 = head1.next;
HeroNode h2 = head2.next;
while (h1 != null && h2 != null) {
if (h1.no < h2.no) {
head.next = h1;
h1 = h1.next;
}else {
head.next = h2;
h2 = h2.next;
}
head = head.next;
}
if (h1 == null) {
head.next = h2;
}
if (h2 == null) {
head.next = h1;
}
return singleLinkedList;
}
思维误区
如果初始化时,三个指针都指向头节点。
思路是这样:每次我们使用指针的next进行比较,找到较小的节点(比如就是p1.next)就移动指针,对应步骤1
之后,我们将p3.next指向p1,对应步骤2
接着,p2.next.no
然后将p3.next = p2
注意:此时的p1这个链表已将发生了变化
这时执行p1.next,指向的是2这个节点,p1再也无法指向3这个节点
正确解法
关键在于,初始化时,p1,p2直接指向第一个元素
当p1.no <= p2.no 时
p3.next = p1
p1 = p1.next
p3 = p3.next
这样,更改p3.next时,就不会导致p1,p2的混乱
package com.atguigu.linkedlist;
import java.util.Stack;
public class SingleLinkedListDemo {
public static void main(String[] args) {
//创建几个节点
HeroNode hero1 = new HeroNode(1, "松江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// SingleLinkedList linkedList = new SingleLinkedList();
// linkedList.addByNo(hero2);
// linkedList.addByNo(hero1);
// linkedList.addByNo(hero4);
// linkedList.addByNo(hero3);
// linkedList.addByNo(hero3);
// linkedList.list();
// System.out.println("---------另一种写法--------------");
// linkedList.add2ByNo(hero2);
// linkedList.add2ByNo(hero1);
// linkedList.add2ByNo(hero4);
// linkedList.add2ByNo(hero3);
// linkedList.add2ByNo(hero3);
// linkedList.list();
HeroNode hero5 = new HeroNode(5, "林冲冲", "豹子头");
HeroNode hero6 = new HeroNode(6, "吴用", "智多星");
// linkedList.update(hero5);
// linkedList.update(hero6);
// linkedList.list();
//
// System.out.println("-----删除后的节点-------");
// linkedList.delete(hero5);
// linkedList.delete(hero6);
// linkedList.list();
// System.out.println("---------获取有效节点的个数----------");
// System.out.println(SingleLinkedList.getLength(linkedList.getHead()));
//
// System.out.println("----------获取链表指定位置的数据-------------");
// System.out.println(linkedList.getK(1));
// System.out.println(linkedList.getK(2));
// System.out.println(linkedList.getK(3));
// System.out.println(linkedList.getK(4));
//
// System.out.println("------------测试删除和添加------------");
// System.out.println(SingleLinkedList.getLength(linkedList.getHead()));
// linkedList.addNode(hero6, 5);
// linkedList.deleteNode(0);
// linkedList.list();
//
// System.out.println("--------输出原始链表------------");
// linkedList.list();
// System.out.println("--------反转这个链表------------");
// SingleLinkedList reverse = SingleLinkedList.reverse(linkedList);
// reverse.list();
// System.out.println("--------经过反转后的原始链表--------");
// linkedList.list();
//
// System.out.println("--------逆序打印链表--------");
// SingleLinkedList.reversePrint(linkedList.getHead());
System.out.println("--------合并两个有序单链表--------");
//创建两个单链表
SingleLinkedList linkedList1 = new SingleLinkedList();
SingleLinkedList linkedList2 = new SingleLinkedList();
//随意添加几个元素
linkedList1.addByNo(hero4);
linkedList1.addByNo(hero6);
linkedList1.addByNo(hero2);
linkedList2.add2ByNo(hero3);
linkedList2.add2ByNo(hero5);
linkedList2.add2ByNo(hero1);
//输出两个链表
System.out.println("--------list1--------");
linkedList1.list();
System.out.println("--------list2--------");
linkedList2.list();
//合并
System.out.println("--------merge--------");
SingleLinkedList mergeList = SingleLinkedList.merge(linkedList1.getHead(), linkedList2.getHead());
mergeList.list();
}
}
//定义SingleLinkedList
class SingleLinkedList {
//定义头节点
private HeroNode head = new HeroNode(0, "", "");
public HeroNode getHead() {
return head;
}
/**
* 合并两个链表
* @param head1
* @param head2
* @return 合并后的新链表
*/
public static SingleLinkedList merge(HeroNode head1, HeroNode head2) {
if (head1.next == null || head2.next == null) {
return null;
}
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode head = singleLinkedList.getHead();
HeroNode h1 = head1.next;
HeroNode h2 = head2.next;
while (h1 != null && h2 != null) {
if (h1.no < h2.no) {
head.next = h1;
h1 = h1.next;
}else {
head.next = h2;
h2 = h2.next;
}
head = head.next;
}
if (h1 == null) {
head.next = h2;
}
if (h2 == null) {
head.next = h1;
}
return singleLinkedList;
}
/**
* 使用stack进行逆序打印
* @param head
*/
public static void reversePrint(HeroNode head) {
//链表空,不打印
if (head.next == null) {
return;
}
Stack<HeroNode> stack = new Stack<>();
while (head.next != null) {
stack.push(head.next);
head = head.next;
}
while (stack.size() > 0) {
System.out.println(stack.pop());
}
}
/**
* 单链表的反转
* @param list 原始单链表
* @return 反转后的单链表
*/
public static SingleLinkedList reverse(SingleLinkedList list) {
int length = getLength(list.head);
if (length < 2) {
System.out.println("链表长度小于2,无需进行反转");
return list;
}
SingleLinkedList newList = new SingleLinkedList();
HeroNode reverseNode;
//这里只要遍历list即可~~懒得改了
while ((reverseNode = list.deleteNode(1)) != null) {
newList.addNode(reverseNode, 1);
}
return newList;
}
/**
* 查找单链表中倒数第k个节点
* @param k
* @return 倒数第K个节点
*/
public HeroNode getK(int k) {
if (k <= 0) {
System.out.println("非法参数");
return null;
}
int length = getLength(head);
int k1 = length - k + 1;
if (k1 <= 0) {
System.out.println("没有倒数第" + k + "个节点");
return null;
}
HeroNode temp = head;
while (k1 != 0) {
temp = temp.next;
k1--;
}
return temp;
}
/**
* 获取单链表节点的个数(有头节点的,不统计头节点)
* @param head 头节点
* @return 有效节点个数
*/
public static int getLength(HeroNode head) {
int length = 0;
while (true) {
if (head.next == null) {
break;
}
length++;
head = head.next;
}
return length;
}
public void delete(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.println("未找到目标节点,删除失败");
}
}
/**
* 删除第i个节点
* @param i
* @return 第i个节点
*/
public HeroNode deleteNode(int i) {
if (i > getLength(head) || i <= 0) {
System.out.println("没有第" + i + "个节点");
return null;
}
//找i前面的节点
i = i - 1;
HeroNode temp = head;
while (i != 0) {
if (temp.next == null) {
break;
}
temp = temp.next;
i--;
}
//将第i个节点存储,作为返回值
HeroNode result = temp.next;
//删除
temp.next = result.next;
return result;
}
public void update(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
heroNode.next = temp.next.next;
temp.next = heroNode;
} else {
System.out.println("未找到目标节点,更新失败");
}
}
//添加节点到单向链表
public void add(HeroNode heroNode) {
//保存头节点
HeroNode temp = head;
//遍历链表,找到最后一个节点
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
//循环跳出时,temp指向最后一个节点,然后将新节点添加即可
temp.next = heroNode;
}
//根据排名进行添加
public void addByNo(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
//如果一直找到最后一个位置,直接添加即可
if (temp.next == null) {
temp.next = heroNode;
break;
}
//当前节点已存在
if (temp.no == heroNode.no) {
flag = true;
break;
}
//找到了位置
if (temp.no < heroNode.no && temp.next.no > heroNode.no) {
heroNode.next = temp.next;
temp.next = heroNode;
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("当前节点已存在,添加失败");
}
}
public void add2ByNo(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no > heroNode.no) {
break;
} else if (temp.next.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("已经存在,不可添加");
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
/**
* 将 Node 添加到第i个位置
* @param node
* @param i
*/
public void addNode(HeroNode node, int i) {
if (i > getLength(head) + 1 || i <= 0) {
System.out.println("参数不合法,无法将当前节点加入链表");
return;
}
//找i前面一个位置
i = i - 1;
HeroNode temp = head;
while (i != 0) {
if (temp.next == null) {
break;
}
temp = temp.next;
i--;
}
//判断是否已经找到
if (i == 0) {
node.next = temp.next;
temp.next = node;
}
}
//显示链表
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
while (true) {
//判断是否到链表最后
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
//定义HeroNode
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 + '\'' +
'}';
}
}