二、链表(Linked List)
链表是以节点(node)存储的链式存储结构,一个node包含一个data域(存放数据)和一个next域(存放下一个node的指针),链表的各个节点不一定是连续的,它可以分为带头结点和不带头结点。头结点仅包含next域。
1、单向链表
1.1顺序插入
-
创建节点类,包含了data和next,next指向下一个节点对象
class PersonNode { public int number; public String name; public String nickName; public PersonNode next; public PersonNode(int number, String name, String nickName) { this.number = number; this.name = name; this.nickName = nickName; } @Override public String toString() { return "PersonNode{" + "number=" + number + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}'; } }
-
创建单向链表类,里面包含了增删改查的方法
-
在链表类创建head Node,头节点的next指向第一个节点
-
实现增删改查的方法
-
在链表中增加一个节点
确定要插入的节点位置,循环遍历到该位置节点的上一个节点。
S->next=P->next P->next=S
- 在链表中删除一个节点
确定删除的位置,循环遍历到该位置节点的上一个节点
P->next=p->next->next
-
遍历链表
PersonNode temp = headNode; while (true) { if (temp.next == null) { break; } temp = temp.next; System.out.println(temp); }
-
修改链表中的一个节点信息
确定要修改节点的位置,先遍历找到该节点,修改该节点的信息。
2、双向链表
双向链表是在单向链表的基础上加入pre指针,因此支持双向的增删改查。
public int number; public String name; public String nickName; public PersonNode next; public PersonNode pre;
- 双向链表的遍历
双向链表的遍历与单向链表相同,可以按照两个方向遍历。
- 双向链表的添加
1、在末尾添加
在双向链表的末尾添加C节点(定位到节点B)
B->next=C C->pre=B
2、在两节点间添加
在节点B和节点C间添加D(程序定位到节点D),
顺序:先搞定D的前驱和后继节点,再搞定C的节点的前驱和B的后继节点。
D->pre=B D->next=C C->pre=D B->next=D
- 双向链表的修改
同单链表修改相同
- 双向链表的删除
1、在链表尾部删除(定位到节点P(B))
2、在两节点间删除(定位到节点S(D))
3、单向循环链表(无头结点)
单向循环链表是首尾相连,使整个单链表构成一个环。约瑟夫问题时单向循环链表的一个典型的应用
约瑟夫问题描述:
约瑟夫问题是一个非常著名的趣题,即由n个人坐成一圈,按顺时针由1开始给他们编号。* 然后由第一个人开始报数,数到m的人出局。现在需要求的是最后一个出局的人的编号。* 给定两个int n和m,代表游戏的人数。请返回最后一个出局的人的编号.
问题分析:
约瑟夫问题可以简单分为两个步骤
构造Boy节点类
private int number;
private Boy next;
- 构建一个单向循环链表
定义两个指针变量First和cur,first表示头结点,cur指向当前节点
构造第一个节点first first = boy;//第一个节点为first first.setNext(first);//仅有一个节点时,first指向first,构成闭环 cur = first;//辅助变量指向first 构造其它节点 cur.setNext(boy);//将新添加的节点设置为当前节点的后继节点 boy.setNext(first);//新添加的节点与first构成闭环 cur=boy;//将当前节点设置为新添加的节点
- 根据约定的规则退出循环链表,直至仅有一个节点
定义两个辅助节点变量first和helper,first指向每一轮要开始的节点,helper指向最后一个节点,当first与helper指向同一个节点时,该循环链表仅剩一个节点
构造函数 moveCycle(int no,int number,int sum) 1、确定从第几个节点开始 for(int i=0;i<no-1;i++){ first=first.getNext(); helper=helper.getNext(); } 2、循环,找出节点,直至仅剩一个节点 while (true){ if(first==helper){ System.out.printf("最后一个节点时%d\n",first.getNumber()); break; } //开始,每number下,出圈一次 for(int j=0;j<number-1;j++){ first=first.getNext(); helper=helper.getNext(); } System.out.printf("%d出圈\n",first.getNumber()); first=first.getNext(); helper.setNext(first); }