定义:环形链表是将 单向链表的首尾相连,可以通过头节点往下再次寻找到头节点。
链表主要作用就是方便删除和增加节点使用的,在查找方面有一定的缺陷。
环形链表主要用于解决约瑟夫问题
约瑟夫问题:
设编号为 1,2,3,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出列编号的序列。
当然这个问题用数组也能够实现,这里仅考虑使用环形链表,打开算法思路和了解环形链表
export default class CricleLinkedList<T>{
private first: Node<T>;
public add(data: T): void {
if (!this.first) {
this.first = new Node(data);
this.first.next = this.first; // 环形链表会将首尾相连
} else {
let temp = this.first;
while (temp.next !== this.first) { // 循环找到最后一个节点赋值给temp
temp = temp.next;
}
temp.next = new Node<T>(data); // 将最后一个节点的next属性指向 添加的数据
temp.next.next = this.first; // 将添加的节点的next属性指向第一个节点,完成首尾相连的环形
}
}
public remove(node: Node<T>): boolean {
let temp = this.first;
let result = false;
while (temp) {
if (temp == node) {
temp.next = temp.next.next;
result = true;
this.showList();
break;
}
temp = temp.next;
}
return result;
}
public getNodeAndRemoveNode(countNum: number, startNo:number): T[] {
let arr = [];
let temp = this.first;
let helper = this.first;
while(true){
if(helper.next == this.first){
break;
}
helper = helper.next;
}
for (let index = 0; index < startNo - 1; index++) { // 第一次同时向后移动找到对应的开始位置
temp = temp.next;
helper = helper.next;
}
while(true){
if(temp == helper){ // 如果helper等于了temp 说明当前链表中只存在了一个数据
temp.next = helper;
arr.push(temp.data);
break;
}
for (let index = 0; index < countNum - 1; index++) { // 通过循环 countNum-1 次完成移动计数
temp = temp.next;
helper = helper.next;
}
console.log("小孩出圈: " , temp.data);
arr.push(temp.data);
temp = temp.next;
helper.next = temp; // 将helper的下一个节点指向第一个节点,完成首尾连接,方便下一个循环使用
}
console.log("最后留存的小孩: " , temp.data);
return arr;
}
public showList() : void{
let temp = this.first;
console.log("--------------------------- 打印链表")
while(true){
console.log(" node :" ,temp.data);
if(temp.next == this.first){
break;
}
temp = temp.next;
}
}
}