单向链表
package com.weeks.linkedlist;
import java.util.Stack;
/**
* @author 达少
* @version 1.0
* 实现链表,保存水浒传英雄人物
*/
public class LinkedListDemo {
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, "李逵", "黑旋风");
//创建链表
// LinkedList linkedList = new LinkedList();
// //添加数据到链表
// linkedList.push(hero1);
// linkedList.push(hero4);
// linkedList.push(hero2);
// linkedList.push(hero3);
//按id升序插入
// linkedList.pushOrderById(hero1);
// linkedList.pushOrderById(hero4);
// linkedList.pushOrderById(hero3);
// linkedList.pushOrderById(hero2);
// //显示链表
// System.out.println("======原来的链表======");
// linkedList.list();
/*
//修改节点
linkedList.update(new HeroNode(2, "吴用", "智多星"));
System.out.println("修改后的链表~~~~");
linkedList.list();
//删除节点
linkedList.delete(1);
// linkedList.delete(4);
linkedList.delete(3);
// linkedList.delete(2);
// linkedList.delete(5);
System.out.println("删除节点后的链表~~~");
linkedList.list();
//测试获取链表测长度
int size = LinkedList.getSize(linkedList.getHead());
System.out.println("当前链表的长度为:" + size);
//测试返回倒数第k个元素
int k = 2;
HeroNode lastK = LinkedList.findLastK(linkedList.getHead(), k);
System.out.println("链表的倒数第" + k + "个元素是:" + lastK);
*/
//反转链表
// LinkedList.reverse(linkedList.getHead());
// System.out.println("======反转后的链表======");
// linkedList.list();
//逆序输出链表元素
// System.out.println("======逆序输出链表元素(不改变链表的结构~)======");
// LinkedList.reversePrint(linkedList.getHead());
//创建两个链表
LinkedList linkedList1 = new LinkedList();
LinkedList linkedList2 = new LinkedList();
//向两个链表中添加数据
linkedList1.pushOrderById(hero1);
linkedList1.pushOrderById(hero3);
linkedList1.pushOrderById(hero5);
linkedList1.pushOrderById(hero7);
linkedList2.pushOrderById(hero2);
linkedList2.pushOrderById(hero4);
linkedList2.pushOrderById(hero6);
System.out.println("原来的链表1:");
linkedList1.list();
System.out.println("原来的链表2:");
linkedList2.list();
//合并两个链表
HeroNode merge = LinkedList.merge(linkedList1.getHead(), linkedList2.getHead());
LinkedList mergeLinkedList = new LinkedList(merge);
System.out.println("合并后的链表:");
mergeLinkedList.list();
System.out.println("原来的链表1:");
linkedList1.list();
System.out.println("原来的链表2:");
linkedList2.list();
}
}
class LinkedList {
//头节点,不存储具体的数据
private HeroNode head = new HeroNode(0, "", "");
public LinkedList(){}
public LinkedList(HeroNode head){
this.head = head;
}
public HeroNode getHead() {
return head;
}
//添加节点(尾插法)
public void push(HeroNode heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p.next == null) {
break;
}
//不空则p向后移,直到找到尾节点
p = p.next;
}
//当退出循环p已经指向尾节点,在p后添加节点
p.next = heroNode;
}
//添加节点的时候要按id的顺序升序添加,如果id已经存在则提示:加入的英雄人物编号已经存在不能添加
public void pushOrderById(HeroNode heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
boolean flag = false;//标志添加的id英雄任务是否已经存在,默认不存在为false
//首先要找到添加的位置
while (true) {//循环遍历,找到添加的位置
if (p.next == null) {//说明已经到了尾节点
break;
}
if (p.next.id > heroNode.id) {//说明已经找到插入的位置,位与p与p.next之间
break;
}
if (p.next.id == heroNode.id) {//说明id已存在
flag = true;
break;
}
//其他情况将p后移
p = p.next;
}
//如果不存在则将节点添加到指定的位置
if (!flag) {
heroNode.next = p.next;
p.next = heroNode;
} else {
System.out.println("加入的英雄人物编号已经存在不能添加!!!");
}
}
//修改链表节点的信息,根据id查找节点,所以id不能改
public void update(HeroNode newHeroNode) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以修改!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head.next;
boolean flag = false;//标志是否找到要修改的节点
//循环遍历俩链表找到要修改的节点
while (true) {
if (p == null) {//说明已经遍历完整链表
break;
}
if (p.id == newHeroNode.id) {
flag = true;
break;
}
//其他情况p后移
p = p.next;
}
//找到修改该的节点后进行修改
if (flag) {
//更改信息
p.name = newHeroNode.name;
p.nickName = newHeroNode.nickName;
} else {
System.out.printf("没有找到id为%d的节点,本次修改不成功!", newHeroNode.id);
}
}
//根据id删除节点
public void delete(int id) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以删除!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
boolean flag = false;//标记是否找到要删除节点的前一个节点
//遍历链表
while (true) {
if (p.next == null) {//说明已经遍历到了尾节点
break;
}
if (p.next.id == id){//说明找到了要删除的节点的前一个节点
flag = true;
break;
}
//其他情况后移
p = p.next;
}
//删除节点
if(flag){
p.next = p.next.next;
}else{
System.out.printf("id为%d的节点不存在,本次删除操作失败!", id);
}
}
//遍历显示整个链表
public void list() {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head.next;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p == null) {
break;
}
//显示节点
System.out.println(p);
//后移
p = p.next;
}
}
//获取链表的长度
public static int getSize(HeroNode head){
//定义变量保存有效节点的个数(头节点不是有效节点)
int size = 0;
//定义变量作为游标,遍历链表
HeroNode cur = head.next;
//遍历链表
while(cur != null){
size++;
//cur后移
cur = cur.next;
}
return size;
}
//获取单链表的倒数第k个节点元素【新浪面试题】
/**
* 分析:
* 1.首先要知道这个链表的总长度size,可以使用getSize()获取
* 2.获取要遍历的长度为size-k
* 3.返回倒数第k个元素
*/
public static HeroNode findLastK(HeroNode head, int k){
//判断链表是否为空
if(head.next == null){
return null;
}
//获取当前链表的长度
int size = getSize(head);
//判断输入的k值是否符合要求
if(k <= 0 || k > size){
return null;
}
//定义临时变量遍历链表
HeroNode cur = head.next;
//遍历前size-k个元素
for (int i = 0; i < (size - k); i++) {
//后移cur
cur = cur.next;
}
return cur;
}
//实现单链表的反转【腾讯的面试题】
/**
* 分析:
* 1.定义一个头节点reverseHead
* 2.从原来的链表中遍历获取节点,每获得一个节点就将其从原来的链表中摘取下来
* 3.将摘取下来的节点插入到新的头节点的下一个位置(头插法)
* 4.最后head.next = reverse.next
*/
public static void reverse(HeroNode head){
//判断当前链表是否为空或者是否只有一个元素
if(head.next == null || head.next.next == null){
return;
}
//定义两个变量,分别指向原来链表的当前节点和当前节点的下一个节点
HeroNode cur = head.next;
HeroNode next = null;
//定义新的头节点
HeroNode reverseHead = new HeroNode(0, "", "");
//遍历原来的链表
while(cur != null){
//将next指向当前节点的下一个节点,记录下一个摘取位置
next = cur.next;
//将cur从原来的列表中摘取下来
head.next = cur.next;
//将摘取下来的节点插入新的头节点的下一个位置
cur.next = reverseHead.next;
reverseHead.next = cur;
//将原来链表的下一个节点赋值给cur
cur = next;
}
//最后将新的头节点next赋值给原来的头节点next,完成反转
head.next = reverseHead.next;
}
//将单链表按原来的顺序逆序打印出来【百度面试题】
/**
* 分析:
* 方式1:将原来的链表进行反转后再进行遍历操作,但是这会改变链表的结构,所以不推荐
* 方式2:利用栈的先进后出特性,反向打印链表,不改变链表的原来结构
*/
public static void reversePrint(HeroNode head){
//判断链表是否为空
if (head.next == null){
return;
}
//定义一个栈
Stack<HeroNode> stack = new Stack<>();
//定义游标遍历链表
HeroNode cur = head.next;
//遍历链表
while(cur != null){
//将当前节点压入栈中
stack.push(cur);
//将cur后移
cur = cur.next;
}
//输出栈中的元素
while(stack.size() > 0){
System.out.println(stack.pop());
}
}
//合并两个有序链表,并保证合并后的链表还是有序的(按id大小升序排序),不能破坏原来的两个链表(深拷贝)
/**
* 分析:
* 1.新建一个头节点:newHead
* 2.比较两个链表的元素id大小,将id小的链表节点添加到新的的链表中
* 3.返回新的链表头节点
*/
public static HeroNode merge(HeroNode head1, HeroNode head2){
//判断链表1,2是否为空
if(head1.next == null && head2.next == null){
return null;
}
if(head1.next == null){
return head2;
}
if(head2.next == null){
return head1;
}
//定义新的头节点
HeroNode new_head = new HeroNode(0, "", "");
//定义两个游标,遍历两个链表
HeroNode cur1 = head1.next;
HeroNode cur2 = head2.next;
//定义变量存储要插入到新链表的元素
HeroNode temp = null;
//定义变量记录新链表的尾部节点
HeroNode rear = new_head;
while(cur1 != null && cur2 != null){
if(cur1.id < cur2.id){
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1向后移
cur1 = cur1.next;
}else if(cur1.id > cur2.id){
temp = new HeroNode(cur2);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur2向后移
cur2 = cur2.next;
}else {//这种情况是cur1.id==cur2.id,所以只允许插入其中一个,选择插入cur1
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1和cur2都要向后移
cur1 = cur1.next;
cur2 = cur2.next;
}
}
//当其中有一个链表还没遍历完,要继续遍历添加到新链表中
while(cur1 != null){
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1和cur2都要向后移
cur1 = cur1.next;
}
while(cur2 != null){
temp = new HeroNode(cur2);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur2向后移
cur2 = cur2.next;
}
return new_head;
}
}
class HeroNode {
public int id;
public String name;
public String nickName;
public HeroNode next;
public HeroNode(int id, String name, String nickName) {
this.id = id;
this.name = name;
this.nickName = nickName;
}
//深拷贝方法
public HeroNode(HeroNode heroNode){
this.id = heroNode.id;
this.name = heroNode.name;
this.nickName = heroNode.nickName;
this.next = null;
}
@Override
public String toString() {
return "HeroNode{" +
"id=" + id +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
双向链表
package com.weeks.linkedlist;
/**
* @author 达少
* @version 1.0
*/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 hero3 = new HeroNode2(3, "武松", "行者");
HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
//创建双向链表
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
//添加
// doubleLinkedList.push(hero1);
// doubleLinkedList.push(hero2);
// doubleLinkedList.push(hero3);
// doubleLinkedList.push(hero4);
//测试按id顺序添加
doubleLinkedList.pushOrderById(hero1);
doubleLinkedList.pushOrderById(hero4);
doubleLinkedList.pushOrderById(hero2);
doubleLinkedList.pushOrderById(hero3);
//显示链表
doubleLinkedList.list();
//修改
doubleLinkedList.update(new HeroNode2(4, "公孙胜", "入云龙"));
System.out.println("修改后的链表~");
doubleLinkedList.list();
//删除
doubleLinkedList.delete(1);
doubleLinkedList.delete(4);
System.out.println("修改后的链表~");
doubleLinkedList.list();
}
}
@SuppressWarnings({"all"})
class DoubleLinkedList{
//头节点,不存储具体的数据
private HeroNode2 head = new HeroNode2(0, "", "");
public HeroNode2 getHead() {
return head;
}
//添加节点(尾插法)
public void push(HeroNode2 heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode2 p = head;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p.next == null) {
break;
}
//不空则p向后移,直到找到尾节点
p = p.next;
}
//当退出循环p已经指向尾节点,在p后添加节点
p.next = heroNode;
heroNode.pre = p;
}
//添加节点的时候要按id的顺序升序添加,如果id已经存在则提示:加入的英雄人物编号已经存在不能添加
public void pushOrderById(HeroNode2 heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode2 p = head;
boolean flag = false;//标志添加的id英雄任务是否已经存在,默认不存在为false
//首先要找到添加的位置
while (true) {//循环遍历,找到添加的位置
if (p.next == null) {//说明已经到了尾节点
break;
}
if (p.next.id > heroNode.id) {//说明已经找到插入的位置,位于p.next之前
break;
}
if (p.next.id == heroNode.id) {//说明id已存在
flag = true;
break;
}
//其他情况将p后移
p = p.next;
}
//如果不存在则将节点添加到p与p.next之间的位置
if (!flag) {
if (p.next != null) {//插入到中间位置
p.next.pre = heroNode;
heroNode.next = p.next;
p.next = heroNode;
heroNode.pre = p;
}else{//插入到最后
p.next = heroNode;
heroNode.pre = p;
}
} else {
System.out.println("加入的英雄人物编号已经存在不能添加!!!");
}
}
//修改链表节点的信息,根据id查找节点,所以id不能改
public void update(HeroNode2 newHeroNode) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以修改!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode2 p = head.next;
boolean flag = false;//标志是否找到要修改的节点
//循环遍历俩链表找到要修改的节点
while (true) {
if (p == null) {//说明已经遍历完整链表
break;
}
if (p.id == newHeroNode.id) {
flag = true;
break;
}
//其他情况p后移
p = p.next;
}
//找到修改该的节点后进行修改
if (flag) {
//更改信息
p.name = newHeroNode.name;
p.nickName = newHeroNode.nickName;
} else {
System.out.printf("没有找到id为%d的节点,本次修改不成功!", newHeroNode.id);
}
}
//根据id删除节点
public void delete(int id) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以删除!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode2 p = head.next;
boolean flag = false;//标记是否找到要删除节点的前一个节点
//遍历链表
while (true) {
if (p == null) {//说明已经遍历到了尾节点
break;
}
if (p.id == id){//说明找到了要删除的节点的前一个节点
flag = true;
break;
}
//其他情况后移
p = p.next;
}
//删除节点
if(flag){
p.pre.next = p.next;
if(p.next != null) {//如果不是尾节点,才执行下面的操作,否则报空指针异常
p.next.pre = p.pre;
}
}else{
System.out.printf("id为%d的节点不存在,本次删除操作失败!", id);
}
}
//遍历显示整个链表
public void list() {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode2 p = head.next;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p == null) {
break;
}
//显示节点
System.out.println(p);
//后移
p = p.next;
}
}
}
class HeroNode2 {
public int id;
public String name;
public String nickName;
public HeroNode2 next;
public HeroNode2 pre;
public HeroNode2(int id, String name, String nickName) {
this.id = id;
this.name = name;
this.nickName = nickName;
}
//深拷贝方法
public HeroNode2(HeroNode heroNode){
this.id = heroNode.id;
this.name = heroNode.name;
this.nickName = heroNode.nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"id=" + id +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
单向循环链表
package com.weeks.linkedlist;
/**
* @author 达少
* @version 1.0
* 解决约瑟夫问题(约瑟夫环)josefu:
* 一群小孩围成一圈形成一个闭环,指定一个小孩作为第一个小孩开始数数,哪个数到m时
* 哪个小孩就出列。问当所有的小孩都出列时,最后的出列顺序是什么?
*/
public class CircleSingleLinkedListDemo {
public static void main(String[] args) {
//创建单项循环链表
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addNode(5);
//显示链表
circleSingleLinkedList.list();
//输出节点出圈的问题
System.out.println("出圈顺序~~");
circleSingleLinkedList.outLinked(1, 2, 5);//2->4->1->5->3
}
}
//创建单项循环链表,表示小孩围成的闭环
class CircleSingleLinkedList{
private Node first = null;//表示指定的第一个小孩
//给定一个数n,创建长度为n的闭环
public void addNode(int n){
//数据校验,判断传如的数据是否
if(n<1){
System.out.println("输入的数据没有意义!");
return;
}
//创建辅助变量,辅助常见环形链表
Node cur = null;
//使用for循环创建环形链表
for (int i = 1; i <= n; i++) {
if(i == 1){//当创建第一个节点时
first = new Node(i);
first.setNext(first);
cur = first;
}else{
Node node = new Node(i);
cur.setNext(node);
node.setNext(first);
cur = cur.getNext();
}
}
}
//遍历链表并显示
public void list(){
//判断链表是否为空
if(first == null){
System.out.println("链表为空,没有任何元素可以遍历!");
return;
}
//创建辅助变量遍历链表
Node cur = first;
while(true){
System.out.println(cur);
if(cur.getNext() == first){
break;
}
//cur后移
cur = cur.getNext();
}
}
//完成node出圈的问题
public void outLinked(int startNo, int step, int nodes){
//数据校验
if(startNo < 0 || step < 0 || startNo > nodes){
System.out.println("输入的数据有误!!!");
return;
}
//定义辅助变量,帮助节点出圈
Node helper = first;
//将helper变量指向链表的最后一个链表
while(helper.getNext() != first){
helper = helper.getNext();
}
//将first移动到id为startNo的节点上,helper移动到first的前一个节点上
for (int i = 0; i < startNo - 1; i++){
first = first.getNext();
helper = helper.getNext();
}
//数数将first和helper移动step-1次
while(true) {
if(first.getNext() == first){
break;
}
for (int i = 0; i < step - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//first已经指向了要出圈的节点,将其输出
System.out.println(first);
first = first.getNext();
helper.setNext(first);
}
//将最后的一个节点输出
System.out.println(first);
}
}
//创建Node类,一个node对象代表一个小孩
class Node{
private int id;
private Node next;
public Node(int id){
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
'}';
}
}