单线无头循环链表代码实现
public class MyHeadlessLinkedList {
// 虽然没有头部但是依旧需要使用一个字段来记录第一个值
private Node head;
//记录链表的长度
private int N;
public MyHeadlessLinkedList(){
}
/**
- 定义节点类
*/
public class Node{
T item ; //存储数据
Node next; //下一个节点
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
/**
- 清空链表
*/
public void clear(){
this.head = null;
this.N = 0;
}
/**
-
判断链表是否为空,是返回true,否返回false
-
@return
*/
public boolean isEmpty(){
return this.N == 0;
}
/**
-
获取链表中元素的个数
-
@return
*/
public int length(){
return this.N;
}
/**
-
读取并返回链表中的第index个元素的值
-
@param index
-
@return
*/
public T get(int index){
if(index > this.N){
System.out.println(“链表长度越界!”);
return null;
}
Node n = this.head;
for (int i = 0; i < index; i++) {
n = n.next;
}
return n.item;
}
public Node getNode(int index){
if(index > this.N){
System.out.println(“链表长度越界!”);
return null;
}
Node n = this.head;
for (int i = 0; i < index; i++) {
n = n.next;
}
return n;
}
/**
-
往链表中添加一个元素
-
@param t
*/
public void insert(T t){
if(isEmpty()){
this.head = new Node(t, null);
}
Node n = this.head;
for (int i = 1; i < this.N ; i++) {
n=n.next;
}
//让当前最后一个结点指向新结点
n.next = new Node(t, this.head);
N ++;
}
/**
-
在链表的第index个元素之前插入一个值为t的数据元素
-
@param index
-
@param t
*/
public void insert(int index,T t){
//找到index位置前一个结点
Node pre = this.head;
for (int i = 0; i <= index - 1 ; i++) {
pre = pre.next;
}
//要找到index位置的结点
Node curr = pre.next;
pre.next = new Node(t,curr);
this.N ++;
}
/**
-
删除并返回链表中第i个数据元素
-
@param index
-
@return
*/
public T remove(int index){
//找到index位置前一个结点
Node curr = null;
if(index == 0){ // index 为0时表示需要重新处理head
curr = head;
head = head.next;
}else {
Node pre = this.head;
for (int i = 0; i < index - 1 ; i++) {
pre = pre.next;
}
//要找到index位置的结点
curr = pre.next;
//找到index位置的下一个结点
pre.next = curr.next;
}
this.N --;
return curr.item;
}
/**
*返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
-
@param t
-
@return
*/
public int indexOf(T t){
Node pre = this.head;
int count = 0;
while (pre.next!=null){
count ++ ;
pre = pre.next;
if(pre.item.equals(t)){
return count;
}
}
return -1;
}
}
案例使用 约瑟夫问题
问题描述:
传说有这样一个故事,在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,第一个人从1开始报数,依次往后,如果有人报数到3,那么这个人就必须自杀,然后再由他的下一个人重新从1开始报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从。于是,约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,从而逃过了这场死亡游戏。
问题转换:
-
41个人坐一圈,第一个人编号为1,第二个人编号为2,第n个人编号为n。
-
编号为1的人开始从1报数,依次向后,报数为3的那个人退出圈;
-
自退出那个人开始的下一个人再次从1开始报数,以此类推;
-
求出最后退出的那个人的编号。
解题思路:
-
构建含有41个结点的单向循环链表,分别存储1~41的值,分别代表这41个人;
-
使用计数器count,记录当前报数的值;
-
遍历链表,每循环一次,count++;
-
判断count的值,如果是3,则从链表中删除这个结点并打印结点的值,把count重置为0
代码实现
- 方式一(链表实现)、
public static void main(String[] args) {
/*
-
"约瑟夫环"问题
-
共S个人,从第F个人开始报数,报数1—N
-
这里初始化的41个,人从第1个开始报数,数到3的出局
-
S 为人数 ,F 为开始人的编号 ,N为需要走的个数
-
最后运行结果:3,6,9,12,15,18,21,24,27,30,33,36,39,1,5,10,14,19,23,28,32,37,41,7,13,20,26,34,40,8,17,29,38,11,25,2,22,4,35,16,31
*/
int S=41;
int F=1;
int N=3;
MyHeadlessLinkedList iml = new MyHeadlessLinkedList();
for (int i = 1; i <= S; i++) {
iml.insert(i);
}
MyHeadlessLinkedList.Node node = iml.getNode(F-1); // 因为下标零开始所以 F-1
MyHeadlessLinkedList.Node temp=null;
int count = 0 ;
while (node.next != node){ //当自己与自己的next相等时,表示剩最后一个了停止循环
count++ ;
if(count == N ){
temp.next = node.next;
System.out.print(node.item+“,”);
count = 0;
}else {
temp = node;
}
node = node.next;
}
System.out.println(node.item+“\n”);
}
方式二
public static void main(String args[]) {
final int N=41,S=1,M=3;
int i = S-1, k = N, g = 1, j;
int a[] = new int[N]; //使用数组存放人;
for(int h = 1; h <= N; h++){
a[h-1] = h; //将编号为h的人存到下标为h-1的数组中
}
do {
i = i + (M -1) ; //计算出圈人下标,因为自身需要报数所以减一
while(i >= k)
i = i-k;
System.out.print(a[i]+“,”);
j = i;
while ( j < k-1) {
a[j] = a[j+1]; //第i个人出圈后将后面的人的编号往前移动,相当于把数到5的踢出局
j++;
}
k–; //圈中人数k-1
g++; //循环次数g+1
}while(g <= N); //最多N轮循环
}
方式三
public static void main(String args[]) {
final int N = 41, S = 1, M = 3; //N为总人数,从第S个人开始报数,报数到M的为出圈
int a[] = new int[N + 1];
int i, j, k;
k = S - 1; //k等于S-1,表示从S开始数出圈人的下标
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j++) {
if (k == N)
k = 1; //当出圈人的下标到末尾时,记数从1开始
else
k++;
if (a[k] == 1) //a[k] 做标记,说明下标为k的人已经出圈了
j–; //j-1, 以保证每次数超过M个
}
a[k] = 1; //标记出圈
System.out.print((k) + “,”); //下标为k的人标号为k
}
}
3. 双向链表
一种更复杂的链表是**“双向链表”或“双面链表”**。每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
直接上代码
public class TowWayLinkedList implements Iterable{
// 首节点
private Node head;
// 尾节点
private Node last;
// 链表长度
private int N;
public TowWayLinkedList(){
this.head = new Node(null,null,null);
this.last = null;
this.N = 0;
}
class Node {
T item;
Node next;
Node pre;
public Node(T item,Node next,Node pre){
this.item = item;
this.next = next;
this.pre = pre;
}
}
/**
- 空置链表
*/
public void clear(){
this.last = null;
this.head = new Node(null,null,null);
this.N = 0;
}
/**
-
判断链表是否为空,是返回true,否返回false
-
@return
*/
public boolean isEmpty(){
return this.N==0;
}
/**
-
获取链表中元素的个数;
-
@return
*/
public int length(){
return this.N;
}
/**
-
:读取并返回链表中的第i个元素的值
-
@param indix
-
@return
*/
public T get(int indix){
Node node = this.head;
for (int i = 0; i < indix; i++) {
node = node.next;
}
return node.item;
}
/**
-
往链表中添加一个元素
-
@param t
*/
public void insert(T t){
if(this.last==null){
last = new Node(t,null,head);
head.next = last;
}else{
Node oldLast = this.last;
Node node = new Node(t,null,oldLast);
oldLast.next = node;
last = node;
}
N++;
}
/**
-
在链表的第i个元素之前插入一个值为t的数据元素
-
@param index
-
@param t
*/
public void insert(int index,T t){
Node node = this.head;
// 获取需要插入数据的上一个节点
for (int i = 0; i < index; i++) {
node = node.next;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/bc6e272a60c2ab76921e2b02f2567141.jpeg)
最后的话
无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!
部分截图:
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
ava开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**[外链图片转存中…(img-tjViPva9-1712128467832)]
[外链图片转存中…(img-MPAFMTqA-1712128467833)]
[外链图片转存中…(img-3yT4U55K-1712128467833)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/bc6e272a60c2ab76921e2b02f2567141.jpeg)
最后的话
无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!
部分截图:
[外链图片转存中…(img-N011HktP-1712128467834)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!