大家好,我是皮皮猫吖!
每文一言:昨天已经过去,明天一切未知,但"今天”是上帝赐给我们的“礼物”。
本篇文章:
本篇文章主要是关于java数据结构与算法的一些基本知识:单链表、双向链表、单向循环链表、约瑟夫环。
正文如下:
1、单链表
1)链表是什么?
链表是有序的列表,但是它在内存中是存储如下:
① 链表是以节点的方式来存储的,是一种链式存储
② 每个链节点包含data域、next 域【指向下一个节点】
③ 发现链表的各个节点不一定是连续存储
④ 链表分为带头节点的链表和没有头节点的链表,需要根据实际的需求来确定
2)单链表的创建【在链表末尾添加数据】:
① 添加数据:
- 创建一个head头节点,用来作为这个单链表的头节点
- 每添加一个节点,就将该节点直接加入到链表最后
② 遍历:
- 通过头节点,创建一个临时变量,用来遍历单链表
package com.data.structure.linkedList;
/**
* @author imppm
* @create 2021-02-10-17:04
*/
public class LinkedListDemo {
public static void main(String[] args) {
HeroNode hero1 = new HeroNode(1,"小猫", "xmbb");
HeroNode hero2 = new HeroNode(2,"臭皮皮", "cpp");
HeroNode hero3 = new HeroNode(3,"皮皮猫吖", "ppmy");
LinkedHeroNode linkedHeroNode = new LinkedHeroNode();
linkedHeroNode.add(hero1);
linkedHeroNode.add(hero2);
linkedHeroNode.add(hero3);
linkedHeroNode.list();
}
}
class LinkedHeroNode{
private final HeroNode head = new HeroNode(0,"","");
//添加节点到单向链表
//1.找到当前链表的最后一个节点
//2.在当前链表的末尾添加上当前节点
public void add(HeroNode heroNode){
HeroNode current = head;
//带头结点的遍历方式
while (current.getNext() != null) {
//找到链表最后
//向后移动
current = current.getNext();
}
//退出循环,此时current指向链表的最后
current.setNext(heroNode);
}
//显示链表【遍历】
public void list(){
if(head.getNext() != null){
HeroNode current = head.getNext();
while(current!=null){
System.out.println(current);
current = current.getNext();
}
}else{
System.out.println("链表为空!");
}
}
}
//定义HeroNode,每个heroNode都是一个节点
class HeroNode{
private int no;
private String name;
private String nickName;
private HeroNode next;//指向下一个heroNode节点
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 + '\'' +
'}';
}
}
3)单链表的创建【根据数据中的序号值添加数据】
① 添加数据:
- 创建一个head头节点,用来作为这个单链表的头节点
- 添加节点,在添加节点之前,需要比较添加节点的数据的序号值大于某个节点,小于另外一个节点,正好添加到中间位置
② 遍历:
- 通过头节点,创建一个临时变量,用来遍历单链表
package com.data.structure.linkedList;
/**
* 单链表添加数据
* @author imppm
* @create 2021-02-28-22:01
*/
public class LinkedListDemo_1 {
public static void main(String[] args) {
LinkedListStudentNode list = new LinkedListStudentNode();
StudentNode student1 = new StudentNode(1, "小猫",'女',null);
StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
list.addStudentByNo(student1);
list.addStudentByNo(student0);
list.list();
}
}
class LinkedListStudentNode{
//单链表的头节点
private final StudentNode head = new StudentNode();
//判断单链表是否为空
private boolean isEmpty(StudentNode studentNode){
return studentNode.getNext() == null;
}
//添加数据到单链表到末尾处
public void add(StudentNode node){
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
}
current.setNext(node);
System.out.println(node+"链表末尾添加数据成功");
}
//按照单链表no值添加数据
public void addStudentByNo(StudentNode studentNode){
//获取单链表头节点
StudentNode current = head;
//用于判断是否可以插入
//false:可以插入
//true:不可以插入,单链表中已存在与该no的数据
boolean flag = false;
while(true){
//判断当前节点的下一个节点是否为空
if(current.getNext()==null){
break;
}
//判断当前节点的下一个节点的no值是否大于要插入的节点的值
if(current.getNext().getNo() > studentNode.getNo()){
//找到需要插入的位置
break;
//判断当前节点的下一个节点的no与要插入节点的no大小是否相等
} else if(current.getNext().getNo() == studentNode.getNo()){
//标记为设置为true,表示不可插入
flag = true;
break;
}
//节点的指针后移
current = current.getNext();
}
//判断当前节点是否可以插入
if(!flag) {
//可以插入
//要插入的节点指向原来节点的下一个节点
studentNode.setNext(current.getNext());
//原来的节点的下一个节点指向要插入的节点
current.setNext(studentNode);
System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
}else{
System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
}
}
//遍历单链表
public void list(){
if(isEmpty(head)){
System.out.println("该单链表为空");
return;
}
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
System.out.println(current);
}
}
}
class StudentNode{
private int no;
private String name;
private char gender;
private StudentNode next;
public StudentNode() {
}
public StudentNode(int no, String name, char gender, StudentNode next) {
this.no = no;
this.name = name;
this.gender = gender;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public StudentNode getNext() {
return next;
}
public void setNext(StudentNode next) {
this.next = next;
}
@Override
public String toString() {
return "StudentNode{" +
"no=" + no +
", name='" + name + '\'' +
", gender=" + gender +
", next=" + next +
'}';
}
}
4)单链表的修改【根据数据中的序号值修改当前序号值对应的数据信息】
package com.data.structure.linkedList;
/**
* 单链表添加数据
* @author imppm
* @create 2021-02-28-22:01
*/
public class LinkedListDemo_1 {
public static void main(String[] args) {
LinkedListStudentNode list = new LinkedListStudentNode();
StudentNode student1 = new StudentNode(1, "小猫",'女',null);
StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
// list.list();
list.addStudentByNo(student1);
list.addStudentByNo(student0);
list.updateStudentByNo(new StudentNode(2,"臭皮皮呀",'男',null));
list.list();
}
}
class LinkedListStudentNode{
//单链表的头节点
private final StudentNode head = new StudentNode();
//判断单链表是否为空
private boolean isEmpty(StudentNode studentNode){
return studentNode.getNext() == null;
}
//添加数据到单链表到末尾处
public void add(StudentNode node){
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
}
current.setNext(node);
System.out.println(node+"链表末尾添加数据成功");
}
//按照单链表no值添加数据
public void addStudentByNo(StudentNode studentNode){
//获取单链表头节点
StudentNode current = head;
//用于判断是否可以插入
//false:可以插入
//true:不可以插入,单链表中已存在与该no的数据
boolean flag = false;
while(true){
//判断当前节点的下一个节点是否为空
if(current.getNext()==null){
break;
}
//判断当前节点的下一个节点的no值是否大于要插入的节点的值
if(current.getNext().getNo() > studentNode.getNo()){
//找到需要插入的位置
break;
//判断当前节点的下一个节点的no与要插入节点的no大小是否相等
} else if(current.getNext().getNo() == studentNode.getNo()){
//标记为设置为true,表示不可插入
flag = true;
break;
}
//节点的指针后移
current = current.getNext();
}
//判断当前节点是否可以插入
if(!flag) {
//可以插入
//要插入的节点指向原来节点的下一个节点
studentNode.setNext(current.getNext());
//原来的节点的下一个节点指向要插入的节点
current.setNext(studentNode);
System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
}else{
System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
}
}
//遍历单链表
public void list(){
if(isEmpty(head)){
System.out.println("该单链表为空");
return;
}
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
System.out.println(current);
}
}
//根据no修改信息
public void updateStudentByNo(StudentNode node){
//头节点
StudentNode current = head;
boolean flag = false;
while(true){
if(current.getNext()==null){
break;
}
if(current.getNext().getNo() == node.getNo()){
flag = true;
break;
}
current = current.getNext();
}
if(flag){
if(current.getNext().getNext()!=null){
node.setNext(current.getNext().getNext());
}
current.setNext(node);
System.out.println("编号为"+node.getNo()+"的数据修改信息成功");
}else{
System.out.println("编号为"+node.getNo()+"的数据修改信息失败");
}
}
}
class StudentNode{
private int no;
private String name;
private char gender;
private StudentNode next;
public StudentNode() {
}
public StudentNode(int no, String name, char gender, StudentNode next) {
this.no = no;
this.name = name;
this.gender = gender;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public StudentNode getNext() {
return next;
}
public void setNext(StudentNode next) {
this.next = next;
}
@Override
public String toString() {
return "StudentNode{" +
"no=" + no +
", name='" + name + '\'' +
", gender=" + gender +
", next=" + next +
'}';
}
}
5)单链表的删除【根据数据中的序号值删除当前序号值对应的数据信息】
① 注意:
- 遍历单链表的时候,需要找到待删除节点的前一个节点。
- 设置前一个节点的Next指向前一个节点的Next节点的Next节点current.setNext(current.getNext().getNext());
- 被删除的节点将会被垃圾回收器回收
package com.data.structure.linkedList;
/**
* 单链表添加数据
* @author imppm
* @create 2021-02-28-22:01
*/
public class LinkedListDemo_1 {
public static void main(String[] args) {
LinkedListStudentNode list = new LinkedListStudentNode();
StudentNode student1 = new StudentNode(1, "小猫",'女',null);
StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
// list.list();
list.addStudentByNo(student1);
list.addStudentByNo(student0);
list.updateStudentByNo(new StudentNode(2,"臭皮皮呀",'男',null));
list.deleteStudentNodeByNo(3);
list.deleteStudentNodeByNo(5);
list.list();
}
}
class LinkedListStudentNode{
//单链表的头节点
private final StudentNode head = new StudentNode();
//判断单链表是否为空
private boolean isEmpty(StudentNode studentNode){
return studentNode.getNext() == null;
}
//添加数据到单链表到末尾处
public void add(StudentNode node){
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
}
current.setNext(node);
System.out.println(node+"链表末尾添加数据成功");
}
//按照单链表no值添加数据
public void addStudentByNo(StudentNode studentNode){
//获取单链表头节点
StudentNode current = head;
//用于判断是否可以插入
//false:可以插入
//true:不可以插入,单链表中已存在与该no的数据
boolean flag = false;
while(true){
//判断当前节点的下一个节点是否为空
if(current.getNext()==null){
break;
}
//判断当前节点的下一个节点的no值是否大于要插入的节点的值
if(current.getNext().getNo() > studentNode.getNo()){
//找到需要插入的位置
break;
//判断当前节点的下一个节点的no与要插入节点的no大小是否相等
} else if(current.getNext().getNo() == studentNode.getNo()){
//标记为设置为true,表示不可插入
flag = true;
break;
}
//节点的指针后移
current = current.getNext();
}
//判断当前节点是否可以插入
if(!flag) {
//可以插入
//要插入的节点指向原来节点的下一个节点
studentNode.setNext(current.getNext());
//原来的节点的下一个节点指向要插入的节点
current.setNext(studentNode);
System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
}else{
System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
}
}
//遍历单链表
public void list(){
if(isEmpty(head)){
System.out.println("该单链表为空");
return;
}
StudentNode current = head;
while (current.getNext()!=null){
current = current.getNext();
System.out.println(current);
}
}
public void updateStudentNodeByNo(StudentNode node){
if(head.getNext() == null){
System.out.println("链表为空");
return;
}
StudentNode current = head.getNext();
boolean flag = false;
while (true){
if(current==null){
break;
}
if(current.getNo() == node.getNo()){
flag = true;
break;
}
current = current.getNext();
}
if(flag) {
current.setName(node.getName());
current.setGender(node.getGender());
System.out.printf("序号为%d的节点,修改成功!\n", node.getNo());
}else {
System.out.printf("无法找到序号为%d的节点,修改失败!\n", node.getNo());
}
}
//根据no修改信息
public void updateStudentByNo(StudentNode node){
//头节点
StudentNode current = head;
boolean flag = false;
while(true){
if(current.getNext()==null){
break;
}
if(current.getNext().getNo() == node.getNo()){
flag = true;
break;
}
current = current.getNext();
}
if(flag){
if(current.getNext().getNext()!=null){
node.setNext(current.getNext().getNext());
}
current.setNext(node);
System.out.println("编号为"+node.getNo()+"的数据修改信息成功");
}else{
System.out.println("编号为"+node.getNo()+"的数据修改信息失败");
}
}
//删除节点的时候需要找到待删除节点的前一个节点
public void deleteStudentNodeByNo(int no){
if(head.getNext()==null){
System.out.println("当前链表为空,无法删除数据!");
return;
}
StudentNode current = head;
boolean flag = false;
while (true){
if(current.getNext()==null){
break;
}
//找到待删除节点的前一个节点
if(current.getNext().getNo() == no){
flag = true;
break;
}
current = current.getNext();
}
if(flag) {
System.out.printf("编号为%d的节点删除成功!\n",no);
current.setNext(current.getNext().getNext());
}else{
System.out.printf("编号为%d的节点无法找到,删除失败!\n",no);
}
}
}
class StudentNode{
private int no;
private String name;
private char gender;
private StudentNode next;
public StudentNode() {
}
public StudentNode(int no, String name, char gender, StudentNode next) {
this.no = no;
this.name = name;
this.gender = gender;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public StudentNode getNext() {
return next;
}
public void setNext(StudentNode next) {
this.next = next;
}
@Override
public String toString() {
return "StudentNode{" +
"no=" + no +
", name='" + name + '\'' +
", gender=" + gender +
", next=" + next +
'}';
}
}
6)单链表的相关面试题
① 获取单链表的长度
第一步:遍历当前单链表
第二步:定义变量,计算单链表中的数据个数,不算头节点
② 获取单链表中倒数索引上的节点
第一步:获取到当前单链表的数据总长度
第二步:根据数据总长度和倒数索引K:length-k
第三步:遍历单链表到length-k长度,即可得到倒数索引节点的数据
③ 反转单链表
-
方法一:递归方法,反转单链表
- 第一步:获取到当前的节点【node节点】,获取node节点中next指向的节点【current节点】
- 第二步:如果current节点等于null
- 设置head节点的next节点指向node节点,然后return
- 第三步:如果current节点不等于null,将current节点作为实参,带入到自己这个函数中
- 第四步:如果node节点为head节点的话:
- current节点的next节点指向null
- node节点不为head节点的话:
- current.setNext(node);
-
方法二:创建新的单链表:反转单链表
- 第一步:创建一个新的单链表头节点new节点、获取旧单链表的头节点old节点,创建一个中间变量temp
- 第二步:遍历旧的单链表:
- 中间变量的值等于old节点next指向的节点 ,temp = old.getNext();
- old节点的next节点指向temp节点的next节点:old.setNext(temp.getNext());
- temp节点的next指向新链表的next节点:temp.setNext(new.getNext());
- 新链表的next节点指向temp节点:new.setNext(temp);
④ 反遍历单链表
- 方式一:递归遍历单链表
- 方式二:使用栈的特性,反向遍历单链表
package com.data.leetcode.day03.LinkedList;
import java.util.Stack;
/**
*
* 含头节点的:
* 获取单链表的长度
* 获取倒数索引上的节点
* 反转单链表
* 反遍历单链表
* @author imppm
* @create 2021-03-02-16:16
*/
public class LinkedListDemo1 {
public static void main(String[] args) {
LinkedListNode list = new LinkedListNode();
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
ListNode listNode6 = new ListNode(6);
list.addNode(listNode1);
list.addNode(listNode2);
list.addNode(listNode3);
list.addNode(listNode4);
list.addNode(listNode5);
list.addNode(listNode6);
System.out.println(list.LinkedListLength());
// System.out.println(list.bottomIndex(1));
// System.out.println(list.bottomIndex(2));
// System.out.println(list.bottomIndex(3));
// System.out.println(list.bottomIndex(4));
// System.out.println(list.bottomIndex(5));
// System.out.println(list.bottomIndex(6));
System.out.println(list.lastIndex(1));
System.out.println(list.lastIndex(2));
System.out.println(list.lastIndex(3));
System.out.println(list.lastIndex(4));
System.out.println(list.lastIndex(5));
System.out.println(list.lastIndex(6));
System.out.println("----------------------------------");
// list.reverseListNode(LinkedListNode.head);
//单链表反转
list.reverseList();
//遍历单链表
list.list();
System.out.println("----------------------------------");
list.reverseListPrint();
//反向遍历单链表
// list.reverseList(LinkedListNode.head);
}
}
class LinkedListNode{
public static final ListNode head = new ListNode();
public void addNode(ListNode listNode){
ListNode current = head;
while (current.getNext() != null) {
current = current.getNext();
}
current.setNext(listNode);
}
public int LinkedListLength(){
ListNode current = head;
int count = 0;
while (current.getNext()!=null){
count++;
current = current.getNext();
}
return count;
}
//链表的倒数第几个数据【简单方法】
//倒数第K个,遍历到总长度 - K 位置
public ListNode lastIndex(int k){
//获取链表的头节点
ListNode current = head;
//获取当前链表的长度
int length = LinkedListLength();
//单链表为空
if(current.getNext() == null){
return null;
}
//查找索引不合理
if(k <= 0 || k > length){
return null;
}
//需要遍历到:length - k
//查找单链表的倒数第3个:6 - 3;
// 0,1,2,3
//倒数第六个,对应的第一个,不进行循环
current = current.getNext();
for (int i = 0; i < length - k; i++){
current = current.getNext();
}
//返回查找到的节点
return current;
}
//倒数第几个数据【麻烦方法】
public ListNode bottomIndex(int k){
ListNode current = head;
int length = LinkedListLength();
boolean flag = false;
int index = 0;
while(true){
if(current.getNext() == null){
flag = true;
break;
}
index++;
current = current.getNext();
if(index == length - k + 1 && length >= k){
break;
}
}
if(flag){
System.out.printf("倒数第%d个节点不存在!\n",k);
return null;
}
return current;
}
//反转单链表方式一
public void reverseListNode(ListNode node){
//1. 获取当前节点的next指向的节点
ListNode current = node.getNext();
//2. 如果当前节点next指向的节点为null,表示当前节点为最后一个节点
//递归到了链表的末尾
if(current == null){
//3. 单链表的头节点指向这个末尾节点
head.setNext(node);
return;
}
//4. 递归当前节点
reverseListNode(current);
//5. 如果当前节点不是头节点,当前节点next指向的节点指向当前节点【反转操作】
if(node != head) {
current.setNext(node);
}else {
//6. 如果当前节点为头节点,当前节点next指向的节点为null【收尾操作】
current.setNext(null);
}
}
//反转单链表方式二
//1. 当前单链表一次减少节点
//2. 另外一个新的单链表,依次增加旧链表减小的节点
// 新单链表增加节点的位置:一直是在单链表的开头的
public void reverseList(){
ListNode current = head;
ListNode newList = new ListNode();
ListNode temp = null;
if(current.getNext()==null || current.getNext().getNext()==null){
return;
}
while (true){
if(current.getNext() == null){
break;
}
//旧链表删除节点
//旧链表依次向后删除节点
//1. 获取当前节点
temp = current.getNext();
//2. 头节点指向当前节点的下一个节点
current.setNext(temp.getNext());
//新链表增加节点
//新链表增加节点,一直是最开始位置
//3. 新增加的节点指向新链表的next指向的节点
temp.setNext(newList.getNext());
//4. 新链表的next指向添加的节点
newList.setNext(temp);
}
//旧链表的next指向新链表的next
head.setNext(newList.getNext());
}
//遍历单链表
public void list(){
ListNode current = head;
while (current.getNext()!=null){
current = current.getNext();
System.out.println(current);
}
}
//反向遍历单链表:方法一:递归方法
public void reverseList(ListNode node){
//当前节点next指向的节点
ListNode current = node.getNext();
//当前节点next指向的节点为null,表示当前节点为最后一个节点
if(current == null){
return;
}
//递归:当前节点
reverseList(current);
//输出当前节点next指向节点对应的值
System.out.println(current.getVal());
}
//反向遍历单链表:方法二:栈方法
public void reverseListPrint(){
ListNode current = head;
if(current.getNext()==null){
return;
}
//创建一个栈
Stack<ListNode> list = new Stack<>();
while (true){
if(current.getNext()==null){
break;
}
//获取单链表节点
current = current.getNext();
//将单链表的数据放到栈中进行存储
list.add(current);
}
//遍历栈
while (list.size()>0){
//输出栈中的数据
System.out.println(list.pop().getVal());
}
}
}
class ListNode{
private int val;
private ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public ListNode getNext() {
return next;
}
public void setNext(ListNode next) {
this.next = next;
}
@Override
public String toString() {
return "ListNode{" +
"val=" + val +
", next=" + next +
'}';
}
}
⑤ 合并两个链表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode current_l1 = l1;
ListNode current_l2 = l2;
ListNode head = new ListNode();
ListNode current = head;
while(current_l1!=null && current_l2!=null){
//一个一个走,不用两个一起走
if(current_l1.val >= current_l2.val){
current.next = current_l2;
current_l2 = current_l2.next;
current = current.next;
}else{
current.next = current_l1;
current_l1 = current_l1.next;
current = current.next;
}
}
//如果current_l1有剩余:直接补到新的单链表上
if(current_l1!=null){
current.next = current_l1;
}
//如果current_l1有剩余,直接补到新的单链表上
else if(current_l2 != null){
current.next = current_l2;
}
return head.next;
}
2、双向链表
1)双向链表的增删改查
① 双向链表中增加数据
- 第一步:遍历双向链表【与单链表遍历相同】
- 第二步:遍历到双向链表的末尾,添加数据
- 第三步:需要添加的节点node,current为双向链表的末尾
- current.next = node;
- node.pre = current;
② 双向链表中删除数据
- 第一步:遍历双向链表【与单链表遍历相同】
- 第二步:遍历到双向链表的该数据位置,current
- 第三步:删除的操作
- current.pre.next = current.next;
- current.next.pre = current.pre;
③ 修改双向链表中的数据【与单链表相同】
④ 查找双向链表中的数据【与单链表相同】
⑤ 通过id值,向双向链表中添加数据
package com.data.structure.twoWayLinkedList;
/**
* @author imppm
* @create 2021-03-04-10:47
*/
public class TwoWayLinkedList {
public static void main(String[] args) {
LinkedListNode list = new LinkedListNode();
Node node1 = new Node(1,4,null,null);
Node node2 = new Node(2,7,null,null);
Node node3 = new Node(3,11,null,null);
Node node4 = new Node(4,5,null,null);
//添加数据
list.addNode(node1);
list.addNode(node2);
list.addNode(node3);
list.addNode(node4);
//遍历双向链表
list.list();
//删除数据
list.deleteNodeByNo(node3);
//遍历双向链表
list.list();
Node node5 = new Node(4,1116,null,null);
//修改双向链表
list.updateNodeByNo(node5);
list.list();
Node node10 = new Node(10,10,null, null);
Node node8 = new Node(8,8,null, null);
list.addNodeByNo(node10);
list.addNodeByNo(node8);
list.addNodeByNo(node8);
list.list();
}
}
class LinkedListNode{
//创建双向链表的头节点
private final Node head = new Node();
//判断当前双向链表是否为空
private boolean isEmpty(){
return head.getNext() == null;
}
public Node getLinkedListHead(){
return head;
}
//在双向链表中添加数据【在单链表的末尾添加数据】
public void addNode(Node node){
Node current = head;
while (current.getNext() != null) {
current = current.getNext();
}
current.setNext(node);
node.setPre(current);
System.out.printf("no为%d的节点数据增加成功!\n",node.getNo());
}
public void addNodeByNo(Node node){
Node current = head;
boolean flag = false;
while (true){
if(current.getNext()==null){
break;
}
current = current.getNext();
if(current.getNo() == node.getNo()){
flag = true;
break;
}
//如果双向链表的no值大于node的no值,插入到搞数据的前面
if(current.getNo() > node.getNo()){
break;
}
}
if(flag){
System.out.printf("no为%d的节点数据添加失败!\n",node.getNo());
}else{
//将新结点的pre指向当前节点的前一个节点
node.setPre(current.getPre());
//当前节点的前一个节点的next指向新的节点
current.getPre().setNext(node);
//新的节点的next指向当前节点
node.setNext(current);
System.out.printf("no为%d的节点数据增加成功!\n",node.getNo());
}
}
//在双向链表中删除数据
public void deleteNodeByNo(Node node){
Node current = head;
boolean flag = false;
while (true){
//末尾数据
if(current.getNext()==null){
break;
}
current = current.getNext();
if(current.getNo() == node.getNo()){
flag = true;
break;
}
}
if(flag){
if(current.getNext()!=null){
current.getPre().setNext(current.getNext());
current.getNext().setPre(current.getPre());
}else{
current.getPre().setNext(null);
}
System.out.printf("no为%d的节点数据删除成功!\n",node.getNo());
}else{
System.out.printf("no为%d的节点没有找到,数据删除失败!\n",node.getNo());
}
}
public void updateNodeByNo(Node node){
Node current = head;
boolean flag = false;
while(true){
if(current.getNext()==null){
break;
}
current = current.getNext();
if(current.getNo() == node.getNo()){
current.setVal(node.getVal());
flag = true;
break;
}
}
if(flag){
System.out.printf("no为%d的节点数据修改成功!\n",node.getNo());
}else{
System.out.printf("no为%d的节点没有找到,数据修改失败!\n",node.getNo());
}
}
public Node queryNodeByNo(int no){
Node current = head;
while (current.getNext() != null) {
current = current.getNext();
if (current.getNo() == no) {
System.out.println("no为" + no + "的节点数据为" + current + "!");
return current;
}
}
return null;
}
public void list(){
Node current = head;
while (current.getNext()!=null){
current = current.getNext();
System.out.println(current);
}
}
}
class Node{
private int no;
private int val;
private Node pre;
private Node next;
public Node() {
}
public Node(int val, Node pre, Node next) {
this.val = val;
this.pre = pre;
this.next = next;
}
public Node(int no, int val, Node pre, Node next) {
this.no = no;
this.val = val;
this.pre = pre;
this.next = next;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public Node getPre() {
return pre;
}
public void setPre(Node pre) {
this.pre = pre;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
@Override
public String toString() {
return "Node{" +
"val=" + val +
'}';
}
}
3、单向环形链表应用:约瑟夫环问题
1)约瑟夫环:
① 约瑟夫环问题分析
② 创建约瑟夫环、遍历约瑟夫环分析
③ 约瑟夫环中:出圈操作分析
④ 解决方法一【较为复杂,出圈问题中,并非真正对应的玩家进行出圈】:
package com.data.leetcode.day03.LinkedList;
import java.util.Scanner;
/**
* @author imppm
* @create 2021-03-05-19:34
*/
public class JosephRing {
public static void main(String[] args) {
Node head = new Node(1,null);
JosephRingNode list = new JosephRingNode();
int num;
int no;
int digit;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入围圈的人数:");
num = scanner.nextInt();
Node node;
for (int i = 0; i < num-1; i++){
node = new Node((i+2),null);
list.addNode(head, node);
}
list.list(head);
System.out.print("请输入约定的编号:");
no = scanner.nextInt();
System.out.print("请输入报的数字:");
digit = scanner.nextInt();
//制作约瑟夫环
list.makeJosephRing(head);
list.count(head, num, no, digit);
}
}
class JosephRingNode{
public void count(Node node, int num, int no, int digit){
Node current = listIndexNode(node, no);
int index = 0;
int i = 1;
while(true){
//当前链表为空,结束循环
if(num == 1){
System.out.println("第"+i+"次输出的数据依次是:"+current.getNo());
break;
}
index++;
if(index == digit){
System.out.println("第"+i+"次输出的数据依次是:"+current.getNo());
current.setNo(current.getNext().getNo());
current.setNext(current.getNext().getNext());
index = 0;
i++;
num--;
continue;
}
current = current.getNext();
}
}
private Node listIndexNode(Node node, int index){
Node current = node;
for (int i = 1; i < index; i++){
current = current.getNext();
}
return current;
}
private boolean isEmpty(Node node){
return node.getNext()==null;
}
public void addNode(Node list, Node node){
Node current = list;
while(current.getNext()!=null){
current = current.getNext();
}
current.setNext(node);
//node.setNext(list);
}
public void list(Node node){
Node current = node;
while(current!=null){
System.out.println(current);
current = current.getNext();
}
}
public void makeJosephRing(Node node){
Node current = node;
while (current.getNext()!=null){
current = current.getNext();
}
current.setNext(node);
}
}
class Node{
private int no;
private Node next;
public Node() {
}
public Node(int no, Node next) {
this.no = no;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", next=" + next +
'}';
}
}
⑤ 解决方法二:【较为简单,出圈问题中,为真正对应玩家的出圈】
package com.data.leetcode.day03.LinkedList;
import java.io.FileReader;
/**
* @author imppm
* @create 2021-03-05-20:40
*/
public class JosephRing2 {
public static void main(String[] args) {
LinkedListNode1 list = new LinkedListNode1();
//创建约瑟夫环
list.createJosephCircle(5);
System.out.println("遍历当前约瑟夫环:");
list.list();
System.out.println("---------------");
System.out.println("出圈的顺序:");
// list.count(10, 3,4);
list.sayCount(5,1,2);
}
}
class LinkedListNode1{
private Node1 head = null;
//直接创建一个num人数的约瑟夫环【环形单链表】
public void createJosephCircle(int nums){
//如果nums小于1,这样的约瑟环不存在
if(nums < 1){
System.out.println("创建的约瑟夫环有问题!");
}
//作为变量,用来记录当前环形单链表的最后一个节点
Node1 curBoy = null;
//创建约瑟夫环
for (int i = 1; i <= nums; i++){
Node1 node1 = new Node1(i, null);
//创建第一个节点的时候,需要单独处理
if(i == 1){
head = node1;
head.setNext(head);
curBoy = head;
}
//其他节点,统一处理
else{
node1.setNext(head);
curBoy.setNext(node1);
curBoy = node1;
}
}
}
//每次添加一个数据到环形单链表中
//添加节点,构建成一个单向环形链表
public void addNode(Node1 node){
Node1 current = head;
//如果当前单链表没有数据,创建单链表的头节点
if(head == null){
head = node;
head.setNext(head);
return;
}
//结束标志
//判断当前链表的下一个节点是不是head节点
while(current.getNext()!=head){
current = current.getNext();
}
current.setNext(node);
node.setNext(head);
}
//遍历当前环形链表
public void list(){
Node1 current = head;
if(current == null){
System.out.println("链表为空");
return;
}
while (current!=null){
System.out.println(current);
current = current.getNext();
if(current==head){
break;
}
}
}
//出圈函数:方式1
//nums:表示总共有多少个人
//start:表示开始数数的位置
//digit:表示数数停止的数字
public void count(int nums, int start, int digit){
//开始数数的小孩的前一个节点
Node1 helper = indexPreNode(start);
//开始数数的小孩节点
Node1 current = helper.getNext();
//当前的报数值
int index = 0;
//当前圈中剩余的人数
int num = nums;
while (true){
//if(first==helper)
if(num == 1){
//圈中还剩下一个人
System.out.println(current);
break;
}
//报数+1
index++;
//当报数的值等于设定的值
if(index == digit){
//设置报数值为0
index = 0;
//当前报数的人出圈
System.out.println(current);
//修改helper的next值
helper.setNext(current.getNext());
//报数的人出圈,current指向下一个人
current = current.getNext();
//圈中的人数-1
num--;
continue;
}
current = current.getNext();
helper = helper.getNext();
}
}
//出圈函数:方式二
//nums:总人数
//start:开始报数位置
//digit:报数截至数
public void sayCount(int nums, int start, int digit){
//如果头节点为null,数据为空,开始节点数据不合理,开始位置大于数据个数
if(head == null || start < 1 || start > nums){
System.out.println("参数输入有误,请重新输入:");
return;
}
//赋值指针
Node1 helper = head;
Node1 current = head;
while (true){
//是否到达最后一个孩子
if(helper.getNext() == head){
break;
}
helper = helper.getNext();
}
//报数前,需要让helper和current移动start-1次
for (int i = 0; i < start - 1; i++){
current = current.getNext();
helper = helper.getNext();
}
//current指向的节点,就是第一个报数的节点
//helper指向的节点,第一个报数的节点的前一个节点
//开始报数,每次需要移动digit-1次,然后出圈
while (true){
//圈中只剩下一个节点
if(current == helper){
break;
}
//current节点和helper节点都向后移动
for (int i = 0; i < digit -1; i++){
current = current.getNext();
helper = helper.getNext();
}
//current指向的节点,就是要出圈的节点
System.out.println(current);
current = current.getNext();
helper.setNext(current);
}
System.out.println(current);
}
//获取最后一个位置节点
public Node1 lastNode(){
Node1 current = head;
while(current!=null){
current = current.getNext();
if(current.getNext()==head){
break;
}
}
return current;
}
//获取到目标位置的前一个节点
public Node1 indexPreNode(int index){
Node1 current = head;
if(index == 1){
return lastNode();
}
for(int i = 2; i < index; i++){
current = current.getNext();
}
return current;
}
}
class Node1{
private int no;
private Node1 next;
public Node1() {
}
public Node1(int no, Node1 next) {
this.no = no;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node1 getNext() {
return next;
}
public void setNext(Node1 next) {
this.next = next;
}
@Override
public String toString() {
return "Node1{" +
"no=" + no +
'}';
}
}
希望本篇文章对大家有所帮助,后续会继续分享java数据结构与算法相关学习知识…
如果文章内容有错误的地方,请在留言处留下你的见解,方便大家共同学习。谢谢!
如有侵权或其他任何问题请联系:QQ1370922071,本文主要用于学习交流,转载请声明!
作者:皮皮猫吖