目录
🎨链表
1.链表( Linked List) 介绍
链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。
特点:
1. 链表是以结点形式存储的,是链式存储
2. 每个结点包含data区域和next区域
3. 如上图各个结点并不是连续存储的
4. 链表分带头结点链表和没有带头结点链表,根据实际的需求来确定
带头结点链表逻辑结构
需求:
根据带有头部的单链表,实现商品增删改查,并且也可以针对商品已编号进行排序,完成排行榜
2. 单链表应用
* author:韩国庆
* date:2021/1/23 14:50
* version:1.0
*/
public class GoodsNode {
public int gId;
public String gName;
public double gProce;
//指针指向下一个结点
public GoodsNode next;
public GoodsNode(int gId, String gName, double gProce) {
this.gId = gId;
this.gName = gName;
this.gProce = gProce;
}
}
* author:韩国庆
* date:2021/1/23 15:23
* version:1.0
*/
public class DLLinkedList {
private GoodsNode node = new GoodsNode(0,"",0.0);
/**
* 添加结点
* @param goodsNode
*/
public void add(GoodsNode goodsNode){
GoodsNode temp = node;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
temp.next = goodsNode;
}
/**
* 插入结点按照id编号插入
*/
public void addByOrder(GoodsNode goodsNode){
GoodsNode temp = node;
boolean flag = false;
while (true){
if (temp.next==null){
break;
}
if (temp.next.gId>goodsNode.gId){
break;
}else if (temp.next.gId==goodsNode.gId){//说明已经
flag = true;
break;
}
temp = temp.next;
}
if (flag){
System.out.println("不能添加该商品,已经存在了...");
}else {
goodsNode.next = temp.next;
temp.next = goodsNode;
}
}
/**
* 修改结点
* @param goodsNode
*/
public void updateNode(GoodsNode goodsNode){
if (node.next==null){
System.out.println("链表为空...");
return;
}
GoodsNode temp = node.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.gId == goodsNode.gId){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.gName = goodsNode.gName;
temp.gProce = goodsNode.gProce;
}else {
System.out.println("没有找到该结点");
}
}
/**
* 删除结点
*
*/
public void delNode(int gId){
GoodsNode temp = node;
boolean flg = false;
while(true){
if (temp.next==null){
break;
}
if (temp.next.gId==gId){
flg = true;
break;
}
temp = temp.next;
}
if (flg){
temp.next = temp.next.next;
}else {
System.out.println("要删除的信息不存在");
}
}
}
3. 常见面试题
给定一个链表,实现倒转链表
if (goodsNode.next==null || goodsNode.next.next == null){
return;
}
GoodsNode point = goodsNode.next;
GoodsNode next = null;
GoodsNode reverseNode = new GoodsNode(0,"",0.0);
while (point !=null){
next = point.next;
point.next = reverseNode.next;
reverseNode.next = point;
point = next;
}
goodsNode.next = reverseNode.next;
}
4. 双向链表应用场景
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
* author:韩国庆
* date:2021/1/25 16:45
* version:1.0
*/
public class GoodsNode2 {
public int gId;
public String gName;
public double gProce;
//指针指向下一个结点
public GoodsNode2 next;
//指针指向上一个结点
public GoodsNode2 pre;
public GoodsNode2(int gId, String gName, double gProce) {
this.gId = gId;
this.gName = gName;
this.gProce = gProce;
}
}
* author:韩国庆
* date:2021/1/25 16:47
* version:1.0
*/
public class TwoLinkedList {
private GoodsNode2 node = new GoodsNode2(0,"",0.0);
/**
*
* @return 返回头结点
*/
public GoodsNode2 returnHeadNode(){
return node;
}
/**
* 添加结点到链表最后面
*/
public void addLast(GoodsNode2 goodsNode2){
GoodsNode2 temp = node;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
temp.next = goodsNode2;
goodsNode2.pre = temp;
}
/**
* 修改结点
*/
public void updateNode(GoodsNode2 newNode){
if (node.next==null){
System.out.println("链表为空");
return;
}
GoodsNode2 temp = node.next;
boolean flg = false;
while (flg){
if (temp == null){
break;
}
if (temp.gId == newNode.gId){
flg = true;
break;
}
temp = temp.next;
}
if (flg){
temp.gName = newNode.gName;
temp.gProce = newNode.gProce;
}else {
System.out.println("没有找到...");
}
}
/**
* 双向链表删除
*/
public void delNode(int gId){
if (node.next == null){
System.out.println("链表为空,无法删除");
return;
}
GoodsNode2 temp = node.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.gId == gId){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.pre.next = temp.next;
if (temp.next !=null){
temp.next.pre = temp.pre;
}
}else {
System.out.println("要删除的结点不存在");
}
}
}
5.单向环形链表应用场景
约瑟夫问题(约瑟夫环)
设编号为1,2…,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
6. 单向环形链表介绍
采用一个单项环形链表来处理(可以带表头也可以不带表头),具体使用头结点还是不带头结点,后面会具体分析。
约瑟问题示意图
· n=5,即有5个人
· k=1,从第一个人开始报数
· m=2,数2下
/**
* @company: 北京动力节点
* @author:韩国庆
*/
public class Boy {
private int no;//编号
private Boy next;//指向下一个节点,默认依旧为空
public Boy(int no){
this.no = no;
}
public void setNo(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
/**
* @company: 北京动力节点
* @author:韩国庆
*/
public class CircleSingleLinkedList {
//创建一个first节点,当前没有编号
private Boy first = new Boy(-1);
//加入小孩节点,构建成环形链表
public void addBoy(int nums){
//判断数据真实性,nums做数据校验
if (nums < 1 ){
System.out.println("nums的值不正确");
return;
}
Boy curBoy = null;//辅助指针,帮助构建环形链表
//使用for循环来创建环形链表
for (int i = 1;i <= nums;i ++){
//根据编号,创建小孩节点
Boy boy = new Boy(i);
//如果是第一个小孩
if (i == 1){
first = boy;
first.setNext(first);//构成环状
curBoy = first;//让curBoy指向第一个小孩
}else {
curBoy.setNext(boy);//前面的指向新来的小孩
boy.setNext(first);//新添加的小孩指向开头,构成环形
curBoy = boy;//后移
}
}
}
//遍历当前的环形链表
public void showBoy(){
//判断链表是否为空
if (first == null){
System.out.println("链表为空~~");
return;
}
//因为first不能动,因此需要辅助指针,完成遍历
Boy curBoy = first;
while (true){
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if (curBoy.getNext() == first){//说明遍历完毕
break;
}
curBoy = curBoy.getNext();//让curBoy后移
}
}
//根据用户的输入,计算出小孩节点出圈的顺序
/*
startNo 表示从第几个小孩开始数数
countNum表示数几下
nums 表示 最初有多少小孩在圈
*/
public void countBoy(int startNo,int countNum,int nums){
//先对数据进行校验
if (first == null || startNo < 1 || startNo > nums){
System.out.println("参数输入有误,请重新输入!");
return;
}
//创建一个辅助指针。帮助我们完成小孩出圈
Boy helper = first;
//需要创建一个辅助指针helper,事先应该指向环形链表的最后的这个节点。
while (true){
if (helper.getNext() == first){//说明小孩指向最后节点
break;
}
helper = helper.getNext();
}
// 报数之前,先让first和helper移动k-1次
for (int j = 0;j < startNo - 1; j ++){
first = first.getNext();
helper = helper.getNext();
}
//当小孩报数时,让first和helper指针同时的移动m-1次
//这里是一个循环的操作,直到圈中只有一个节点
while (true){
if (helper == first){ //说明圈中只有一个节点
break;
}
//否则让first与helper移动countNum-1次
for (int j = 0;j < countNum - 1;j ++){
first = first.getNext();
helper = helper.getNext();
}
//这时first指向的节点,就是要出圈小孩的节点
System.out.printf("小孩%d出圈\n",first.getNo());
//这时将first指向的小孩节点出圈
first = first.getNext();
helper.setNext(first);//
}
System.out.printf("最后留在圈中的小孩编号是%d\n",first.getNo());
}
}
public class TestApp {
public static void main(String[] args) {
//测试,构建环形与遍历是否可行
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);//添加五个小孩节点
circleSingleLinkedList.showBoy();
//测试小孩出圈是否正确
circleSingleLinkedList.countBoy(1,2,5);//从第一个开始,每两个小孩出去一次,一共有五个小孩
}
}
最后
目前市面上更多的是C语言,C++版的数据结构和算法,极少有关于Java数据结构和算法的课程,所以Java程序员往往需要跨语言学习,难度和效率大大折扣!
今天,也分享给大家一套Java的数据结构和算法教程,一套属于咱Java程序员的数据结构和算法课程,帮助广大Java程序员,系统化深度学习数据结构和算法,掌握其中要领实现华丽转身,进大厂,升职加薪指日可待!
https://www.bilibili.com/video/BV1HQ4y1d7th