本实例主要是对java中的单链表进行操作
package com.bigdata.datastructure.linkedlist;
import java.util.Stack;
/**
* @ description:
* @ author: spencer
* @ date: 2020/10/27 10:16
*/
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, "赵信", "菊花");
HeroNode hero5 = new HeroNode(5, "盖伦", "德玛");
HeroNode hero6 = new HeroNode(6, "吴用", "智多星");
HeroNode hero7 = new HeroNode(7, "吕布", "皇子");
HeroNode hero8 = new HeroNode(8, "盲僧", "瞎子");
// 创建链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
SingleLinkedList singleLinkedList2 = new SingleLinkedList();
// 不按照编号添加
// singleLinkedList.add(hero1);
// singleLinkedList.add(hero2);
// singleLinkedList.add(hero6);
// singleLinkedList.add(hero4);
//
// singleLinkedList2.add(hero8);
// singleLinkedList2.add(hero5);
// singleLinkedList2.add(hero7);
// singleLinkedList2.add(hero3);
// 按照编号顺序添加
singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero2);
singleLinkedList.addByOrder(hero6);
singleLinkedList.addByOrder(hero3);
singleLinkedList2.addByOrder(hero5);
singleLinkedList2.addByOrder(hero4);
singleLinkedList2.addByOrder(hero7);
singleLinkedList2.addByOrder(hero8);
System.out.println("修改之前的链表singleLinkedList:");
singleLinkedList.list();
System.out.println("修改之前的链表singleLinkedList2:");
singleLinkedList2.list();
// HeroNode newHero = new HeroNode(2, "小卢2", "玉麒麟嘻嘻");
// singleLinkedList.update(newHero);
// System.out.println("修改后的链表:");
// singleLinkedList.list();
// singleLinkedList.delete(4);
// System.out.println("根据编号删除后的链表:");
// singleLinkedList.list();
//
// // 测试单链表中有效节点的个数
// HeroNode head = singleLinkedList.getHead();
// System.out.println("有效的节点个数 = " + getLength(head));
//
// // 测试单链表中倒数第k个节点
// System.out.println("result = " + findLastIndexNode(head, 4));
// 测试单链表的翻转
// System.out.println("单链表的翻转:");
// reverseList(singleLinkedList.getHead());
// singleLinkedList.list();
// 测试单链表的逆序输出
// System.out.println("测试单链表的逆序输出,没有改变链表的结构");
// reversePrintList(singleLinkedList.getHead());
// 测试两个无序单链表的合并,合并之后的链表有序
System.out.println("测试两个无序单链表的合并,合并之后有序");
HeroNode mergeHead = merge(singleLinkedList.getHead(), singleLinkedList2.getHead());
while (mergeHead.next != null){
System.out.println(mergeHead.next);
mergeHead = mergeHead.next;
}
}
/**
* 实现两个单链表的合并,每个单链表无序,合并后的单链表输出有序。
* 思路:
* 1.先将两个单链表转换成有序的单链表addByOrder
* 1.创建一个新的链表,用于存放从单链表中取出的数据
* 2.遍历两个单链表,将编号较小的数据存入新链表中
* @param head1 第一个无序单列表
* @param head2 第二个无序单列表
* @return 返回新的有序列表的头节点
*/
public static HeroNode merge(HeroNode head1, HeroNode head2){
// 创建一个新链表,用于存放两个单链表中取出的数据
SingleLinkedList singleLinkedList = new SingleLinkedList();
if (head1.next == null && head2.next == null){
return null;
}
if (head1.next == null){
return head2;
}
if (head2.next == null){
return head1;
}
HeroNode temp1 = head1.next;
HeroNode temp2 = head2.next;
// 遍历两个单链表
while (temp1 != null && temp2 != null) {
if (temp1.no <= temp2.no) {
singleLinkedList.add(new HeroNode(temp1.no, temp1.name, temp1.nickName));
temp1 = temp1.next;
} else {
singleLinkedList.add(new HeroNode(temp2.no, temp2.name, temp2.nickName));
temp2 = temp2.next;
}
}
// 如果temp1为空,则直接插入temp2
if (temp1 == null){
while (temp2 != null){
singleLinkedList.add(new HeroNode(temp2.no, temp2.name, temp2.nickName));
temp2 = temp2.next;
}
}
// 如果temp2为空,则直接插入temp1
if (temp2 == null){
while (temp1 != null){
singleLinkedList.add(new HeroNode(temp1.no, temp1.name, temp1.nickName));
temp1 = temp1.next;
}
}
return singleLinkedList.getHead();
}
/**
* 单链表的逆序打印【百度面试题】
* 思路:
* 1.先将链表翻转,然后遍历输出,但是会破坏原来链表的结构,不建议
* 2.使用栈,遍历原来的链表,将遍历出来的节点压入栈中,利用栈的先进后出特点进行打印
*/
public static void reversePrintList(HeroNode head){
if (head.next == null || head.next.next == null){
return;
}
HeroNode temp = head;
Stack<HeroNode> stack = new Stack<HeroNode>();
// 将链表的所有节点压入栈
while (temp.next != null){
stack.push(temp.next);
temp = temp.next;
}
// 弹出栈中的元素
while (stack.size() > 0){
System.out.println(stack.pop());
}
}
/**
* 单向链表翻转【腾讯面试】
* 思路:
* 遍历原来的链表,每遍历一个元素,将其取出,添加到新链表reverseHead 的最前端
* @param head
* @return
*/
public static void reverseList(HeroNode head){
// 如果链表为空或者只有一个元素,则直接返回
if(head.next == null || head.next.next ==null){
return ;
}
// 定义一个辅助的变量,用来遍历原来的链表
HeroNode cur = head.next;
HeroNode next = null; // 指向当前节点【cur】的下一个节点
HeroNode reverseHead = new HeroNode(0, "", "");
// 遍历原来的链表,每遍历一个元素,将其取出,添加到新链表reverseHead 的最前端
while (cur != null){
next = cur.next; // 先保存当前节点的下一个节点(因为取出当前节点之后,下一次取出的节点需要记录)
cur.next = reverseHead.next; // 将cur的下一个节点指向新链表的最前端
reverseHead.next = cur; // 将cur连接到新的链表上
cur = next; // 移动cur,方便下次继续取出
}
// 最后将head.next = reverseHead.next,实现翻转
head.next = reverseHead.next;
}
/**
* 查找单链表中倒数第k个节点【新浪面试题】
* 思路
* 1.编写一个方法,接收heda节点,同时接收一个index
* 2.index表示倒数第index节点
* 3.先把链表从头到尾遍历,得到链表的总长度getLength
* 3.得到size后,我们从链表的第一个开始遍历到第(size-index)个,就可以满足需求
*
* @param head
* @param index
* @return
*/
public static HeroNode findLastIndexNode(HeroNode head, int index){
if (head.next == null){
return null;
}
int size = getLength(head);
// 对index进行校验
if (index < 0 || index > size){
return null;
}
HeroNode cur = head.next;
for (int i = 0; i < size - index; i++) {
cur = cur.next;
}
return cur;
}
/**
* 获取单链表节点的个数(如果有头结点,不统计,因为头节点不存储数据)
* @param head 链表的头结点
* @return 返回的是链表中有效节点的个数
*/
public static int getLength(HeroNode head){
if (head.next == null){
return 0;
}
HeroNode temp = head;
int length = 0;
while (temp.next != null){
length++;
temp = temp.next;
}
return length;
}
}
// 创建单链表管理HeroNode对象(不考虑编号顺序,每次在链表最后添加节点)
class SingleLinkedList{
// 先初始化一个头节点,不存放具体的数据
private HeroNode head = new HeroNode(0, "", "");
// 返回头结点
public HeroNode getHead() {
return head;
}
// 添加节点到链表
// 思路:找到链表的最后一个节点,将最后一个节点的next指向该节点
public void add(HeroNode heroNode){
// 因为head节点不能动,所有需要一个辅助变量来替代head节点
HeroNode temp = head;
while (true){
if (temp.next == null){
break;
}
// 如果没有找到,将temp后移
temp = temp.next;
}
// 当退出while循环时,temp就指向了链表的最后
temp.next = heroNode;
}
// 第二种添加方式,通过排名添加节点到链表指定位置
// 如果有这个排名,则添加失败,并给出提示信息
public void addByOrder(HeroNode heroNode){
// 头节点不能移动,需要使用辅助变量
// 因为需要找到的temp是添加位置的前一个节点,否则添加节点失败
HeroNode temp = head;
boolean flag = false; // 表示添加的编号是否存在,默认为false
while (true){
if (temp.next == null){ // 遍历到链表的最后
break;
}else if (temp.next.no > heroNode.no){ // 找到添加的位置
break;
}else if (temp.next.no == heroNode.no){ // 编号已经存在
flag = true;
break;
}
// 将辅助变量后移遍历
temp = temp.next;
}
// 判断flag的值
if (flag){ // 说明编号已经存在
System.out.printf("需要添加的英雄编号 %d 已经存在,不能继续添加", heroNode.no);
return;
} else {
// 可以插入到链表中
heroNode.next = temp.next;
temp.next = heroNode;
}
}
// 根据编号修改heroNode对象
public void update(HeroNode heroNode){
if (head.next == null){
return;
}
// 找到需要修改的节点(不需要找到修改节点的前一个节点)
HeroNode temp = head;
boolean flag = false; // 表示是否找到该节点
while (true){
if (temp == null){ // 已经遍历完链表
break;
}
if (temp.no == heroNode.no){
// 找到
flag = true;
break;
}
temp = temp.next;
}
// 根据flag判断是否找到要修改的节点
if(flag){
temp.name = heroNode.name;
temp.nickName = heroNode.nickName;
} else {
// 没有找到
System.out.printf("没有找到编号为 %d 的节点,不能修改\n", heroNode.no);
}
}
// 删除节点
public void delete(int no){
if (head.next == null){
System.out.println("链表为空,不能删除数据");
return;
}
HeroNode temp = head;
boolean flag = false;
while (true){
if (temp.next == null){ //遍历完链表都没找到
break;
}
if (temp.next.no == no){
flag = true;
break;
}
temp = temp.next;
}
// 根据编号找到要删除的节点
if (flag){
temp.next = temp.next.next;
} else {
System.out.println("要删除的节点编号不存在,无法删除");
}
}
// 显示链表(遍历)
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,每一个节点就是一个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 + '\'' +
'}';
}
}