数据结构与算法学习笔记(3)——双向链表和环形链表
1.单向链表
1.1.单向链表的缺点分析
(1)查找的方向只能是一个,而双向链表可以向前或向后查找
(2)单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除
class Node{
public int num;
public String name;
public Node pre;
public Node next;
public Node(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Node{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
1.2.双向链表的遍历
遍历方法和单向链表一样,只是可以向前也可以向后查找
//遍历双向链表
public void list(){
if (head.next == null){
System.out.println("链表为空");
return;
}
Node temp = head.next;
while (true){
if (temp == null){
break;
}
System.out.println(temp);
temp = temp.next;
}
}
1.3.双向链表的添加
(1)先找到双向链表的尾节点
(2)temp.next = newNode;
(3)newNode.pre = temp;
//末尾添加节点
public void add(Node node){
Node temp = head;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
temp.next = node;
node.pre = temp;
}
1.4.双向链表的删除
(1)直接实现自我删除某个节点
(2)temp.pre.next = temp.next;
(3)temp.next.pre = temp.pre;
//删除一个节点
public void del(int num){
if (head.next == null){
System.out.println("空链表,不能删除");
return;
}
Node temp = head.next;
boolean flag = false;
while (true){
if(temp == null){
break;
}
if (temp.num == num){
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("没找到该节点");
}
}
1.5.双向链表的修改
//修改一个节点的内容
public void update(Node node){
if(head.next == null){
System.out.println("空链表");
return;
}
Node temp = head;
boolean flag = false;
while (true){
if(temp == null){
break;
}
if(temp.num == node.num){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.name = node.name;
}
}
1.6.双向链表按顺序添加
//按标号顺序添加节点
public void addByOrder(Node node){
Node temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}
if(temp.next.num > node.num){
break;
}
if(temp.next.num == node.num){
flag =true;
break;
}
temp = temp.next;
}
if(flag){
System.out.println("已存在不能添加");
return;
}else{
if(temp.next == null){
temp.next = node;
node.pre = temp;
}else{
node.next = temp.next;
temp.next.pre = node;
temp.next = node;
node.pre = temp;
}
}
}
2.环形链表
应用场景:约瑟夫问题
设编号为1,2,3…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的人再次出列,以此类推直到所有人出列,由此产生的一个出队编号的序列。
2.1 环形链表的创建
(1)先创建一个节点,让first指向该节点,并让first指向自己即first.next = first
(2)每当我们创建一个新节点,便把新节点加入到已有的环形链表
class CircleSingleLinkedList{
//创建一个first节点,当前没有编号
private Node1 first = new Node1(-1);
//添加小孩节点,构建成环形链表
public void add(int nums){
//校验nums
if (nums < 2){
System.out.println("nums的值不正确");
return;
}
Node1 temp = null;
//使用for循环来创建环形链表
for(int i = 1;i<=nums;i++){
//根据编号创建新节点
Node1 node1 = new Node1(i);
//如果是第一个小孩
if(i == 1){
first = node1;
first.next = first; //构成环
temp = first; //让temp指向第一个节点
}else{
temp.next = node1;
node1.next = first;
temp = temp.next;
}
}
}
//根据用户的输入,计算出小孩出圈的顺序\
/**
*
* @param startnum 表示第几个小孩开始数数
* @param countnum 表示数几下
* @param nums 表示最初有多少小孩在圈中
*/
public void count(int startnum,int countnum,int nums){
//先对数据校验
if(first == null || startnum < 1 || startnum > nums){
System.out.println("参数输入有误请重新输入");
return;
}
//创建辅助指针,并让他指向环形链表的最后一个节点
Node1 temp = first;
while (true){
if (temp.next == first){
break;
}
temp = temp.next;
}
//报数前先让first和temp移动startnum-1次
for (int j = 0;j < startnum-1;j++){
first = first.next;
temp = temp.next;
}
//当小孩报数时,让first和temp同时移动countnum-1次,然后出圈
//循环直到圈中只有一个节点
while (true){
if (temp == first){//环形链表只有一个节点
System.out.println("最后出圈的小孩为"+first.num);
break;
}
//让first和temp同时移动countnum-1次,出圈
for (int i = 0;i < countnum-1;i++){
first = first.next;
temp = temp.next;
}
//此时first指向出圈的节点
System.out.println("小孩"+first.num+"出圈");
//小孩出圈
first = first.next;
temp.next = first;
}
}
}
2.2 遍历环形链表
(1)创建一个临时指针temp指向first
(2)遍历环形链表,使得temp.next == first 时结束
//遍历当前环形链表
public void list(){
//判断链表是否为空
if (first == null){
System.out.println("链表为空");
return;
}
//因为first不能动,因此创建一个临时指针temp
Node1 temp = first;
while (true){
System.out.println("小孩的编号"+temp.num);
if (temp.next == first){
System.out.println("遍历完毕");
break;
}
temp = temp.next;
}
}
2.3 约瑟夫问题的出圈编号
//根据用户的输入,计算出节点出圈的顺序\
/**
*
* @param startnum 表示第几个节点开始数数
* @param countnum 表示数几下
* @param nums 表示最初有多少节点在圈中
*/
public void count(int startnum,int countnum,int nums){
//先对数据校验
if(first == null || startnum < 1 || startnum > nums){
System.out.println("参数输入有误请重新输入");
return;
}
//创建辅助指针,并让他指向环形链表的最后一个节点
Node1 temp = first;
while (true){
if (temp.next == first){
break;
}
temp = temp.next;
}
//报数前先让first和temp移动startnum-1次
for (int j = 0;j < startnum-1;j++){
first = first.next;
temp = temp.next;
}
//当小孩报数时,让first和temp同时移动countnum-1次,然后出圈
//循环直到圈中只有一个节点
while (true){
if (temp == first){//环形链表只有一个节点
System.out.println("最后出圈的小孩为"+first.num);
break;
}
//让first和temp同时移动countnum-1次,出圈
for (int i = 0;i < countnum-1;i++){
first = first.next;
temp = temp.next;
}
//此时first指向出圈的节点
System.out.println("小孩"+first.num+"出圈");
//节点出圈
first = first.next;
temp.next = first;
}
}
}