一、特点
- 链表是以节点的方式来存储,是链式存储。
- 每个节点包含data域和next域,next域指向下一个节点。
- 链表的各个节点不一定是连续存放的。
- 链表分为带头结点的链表和没有头结点的链表,根据实际需求确定。
内存结构图
逻辑结构图
二、代码实现
package a03.linkedList;
public class SingleLinkedList {
//先初始化一个头结点,头结点不动,不存放具体的数据,用来表示单链表头
private HeroNode head = new HeroNode(0, "", "");
//添加节点,直接加到末尾
public void add(HeroNode heroNode) {
//因为head节点不能动,需要一个辅助节点
HeroNode temp = head;
//遍历链表找到最后
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
//将节点接入链表
temp.next = heroNode;
}
//添加节点,按编号顺序添加
public void addByOrder(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false; //标志添加的编号是否存在
while (true) {
if (temp.next == null) {
break;
}
//因为是单链表,找的temp是位于添加位置的前一个节点。
if (temp.next.no > heroNode.no) {
break;
} else if (temp.next.no == heroNode.no) {
flag = true;
break;
} else {
temp = temp.next;
}
}
if (flag) {
System.out.println("不能添加,编号已存在: " + heroNode);
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//修改链表
public void update(HeroNode hero) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head;
//标志有没找到对应的节点
boolean flag = false;
while (true) {
if (temp == null) {
break;
}
if (temp.no == hero.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.name = hero.name;
temp.nickName = hero.nickName;
} else {
System.out.println("没有找到该编号的节点:" + hero);
}
}
//删除节点
public void delete(int no){
HeroNode temp = head;
boolean flag = false; //标志要删除的编号是否存在
while (true) {
if (temp.next == null) {
break;
}
//因为是单链表,找的temp是位于添加位置的前一个节点。
if (temp.next.no == no) {
flag = true;
break;
} else if (temp.next.no > no) {
break;
} else {
temp = temp.next;
}
}
if (!flag) {
System.out.println("不能添加,编号不存在: " + no);
} else {
temp.next = temp.next.next;
}
}
//显示链表
public void list() {
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
while (true) {
if (temp == null) {
break;
} else {
System.out.println(temp);
temp = temp.next;
}
}
}
public static void main(String[] args) {
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
HeroNode updateHero = new HeroNode(2, "小卢", "玉麒麟");
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero2);
singleLinkedList.addByOrder(hero4);
singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero1);
singleLinkedList.update(updateHero);
singleLinkedList.delete(2);
singleLinkedList.list();
}
}
class HeroNode {
int no;
String name;
String nickName;
HeroNode next;
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
三、练习题
3.1 求单链表中节点的个数
//获取单链表有效节点个数
public int size(HeroNode head){
//如果带头节点的链表,不统计头结点
if(head.next == null){
return 0;
}
int count = 0;
HeroNode temp = head.next;
while(true){
if(temp == null){
break;
}
count++;
temp = temp.next;
}
return count;
}
3.2 求单链表中倒数第k个节点
//查找单链表中倒数第n个节点
public HeroNode reciprocal(HeroNode head, int index) {
//获取单链表有效节点个数
int size = size(head);
if(index <=0 || index >size){
return null;
}
HeroNode temp = head.next;
for(int i=0; i<size-index; i++){
temp = temp.next;
}
return temp;
}
3.3 单链表的反转
//反转链表
public void reverse(HeroNode head){
if(head.next == null || head.next.next ==null){
return ;
}
//帮助遍历原链表
HeroNode temp = head.next;
HeroNode next = null;
//另建一个链表头来保存结果
HeroNode reverseHead = new HeroNode(0,"","");
while(true){
if(temp == null){
break;
}
next = temp.next;
temp.next = reverseHead.next;
reverseHead.next = temp;
temp = next;
}
head.next = reverseHead.next;
}
3.4 从尾到头打印单链表
//从尾到头打印链表
public void showReverse(HeroNode head){
if(head == null){
return;
}
HeroNode temp = head.next;
Stack<HeroNode> stock = new Stack<>();
while(true){
if(temp == null){
break;
}
stock.push(temp);
temp = temp.next;
}
while (!stock.isEmpty()){
System.out.println(stock.pop());
}
}
3.5 合并两个有序的单链表
//合并两个有序的单向链表
public HeroNode merge(HeroNode head1, HeroNode head2){
if(head1 == null){
return head2;
}
if(head2 == null){
return head1;
}
HeroNode result = new HeroNode(0,"","");
HeroNode temp1 = head1.next;
HeroNode temp2 = head2.next;
while(true){
if(temp1 == null || head2 == null) {
break;
}else if(temp1.no <= head2.no){
result.next=temp1;
temp1 = temp1.next;
}else{
result.next=head2;
temp2 = temp2.next;
}
}
while(true){
if(temp1 == null){
break;
}
result.next=temp1;
temp1 = temp1.next;
}
while(true){
if(temp2 == null){
break;
}
result.next=temp2;
temp2 = temp2.next;
}
return result.next;
}
四、双向链表
4.1 单链表的不足
- 查找的方向只能是一个,而双向链表可以向前或者向后查找。
- 单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除。
4.2 代码实现
package a03.linkedList;
public class DoubleLinkedList {
//头结点
private PersonNode head = new PersonNode(0, "", "");
public PersonNode getHead() {
return head;
}
//显示链表
public void list() {
if (head.next == null) {
System.out.println("链表为空");
return;
}
PersonNode temp = head.next;
while (true) {
if (temp == null) {
break;
} else {
System.out.println(temp);
temp = temp.next;
}
}
}
//添加节点,直接加到末尾
public void add(PersonNode person) {
//因为head节点不能动,需要一个辅助节点
PersonNode temp = head;
//遍历链表找到最后
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
//将节点接入链表,形成双向链表
temp.next = person;
person.pre = temp;
}
//添加节点,按编号顺序添加
public void addByOrder(PersonNode person) {
PersonNode temp = head;
boolean flag = false; //标志添加的编号是否存在
while (true) {
if (temp.next == null) {
break;
}
//因为是单链表,找的temp是位于添加位置的前一个节点。
if (temp.no > person.no) {
break;
} else if (temp.no == person.no) {
flag = true;
break;
} else {
temp = temp.next;
}
}
if (flag) {
System.out.println("不能添加,编号已存在: " + person);
} else {
person.next = temp;
person.pre = temp.pre;
temp.pre.next = person;
temp.pre = person;
}
}
//修改链表
public void update(PersonNode person) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
PersonNode temp = head;
//标志有没找到对应的节点
boolean flag = false;
while (true) {
if (temp == null) {
break;
}
if (temp.no == person.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.name = person.name;
temp.nickName = person.nickName;
} else {
System.out.println("没有找到该编号的节点:" + person);
}
}
//删除节点
public void delete(int no) {
if (head.next == null) {
System.out.println("链表为空,无法删除");
return;
}
PersonNode temp = head;
boolean flag = false; //标志要删除的编号是否存在
while (true) {
if (temp == null) {
break;
}
//因为是单链表,找的temp是位于添加位置的前一个节点。
if (temp.no == no) {
flag = true;
break;
} else if (temp.no > no) {
break;
} else {
temp = temp.next;
}
}
if (!flag) {
System.out.println("不能添加,编号不存在: " + no);
} else {
temp.pre.next = temp.next;
//删除最后一个节点的情况
if(temp.next != null){
temp.next.pre = temp.pre;
}
}
}
public static void main(String[] args) {
PersonNode hero1 = new PersonNode(1, "宋江", "及时雨");
PersonNode hero2 = new PersonNode(2, "卢俊义", "玉麒麟");
PersonNode hero3 = new PersonNode(3, "吴用", "智多星");
PersonNode hero4 = new PersonNode(4, "林冲", "豹子头");
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.add(hero1);
doubleLinkedList.add(hero2);
doubleLinkedList.add(hero4);
doubleLinkedList.addByOrder(hero3);
System.out.println("----添加----");
doubleLinkedList.list();
hero2.name="小卢";
doubleLinkedList.update(hero2);
System.out.println("----更新----");
doubleLinkedList.list();
doubleLinkedList.delete(1);
doubleLinkedList.delete(2);
doubleLinkedList.delete(3);
doubleLinkedList.delete(4);
System.out.println("----删除----");
doubleLinkedList.list();
}
}
class PersonNode {
int no;
String name;
String nickName;
PersonNode next;
PersonNode pre;
public PersonNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
五、单向循环链表
5.1 约瑟夫(Josephu)问题
- 设编号为1,2,3...n的n个人围坐一圈,约定编号为k的人从1开始报数,数到m的那个人出列。
- 出列人的下一位又从1开始报数,数到m的那个人出列。
- 以此类推,直到所有人出列,产生一个出队编号的序列。
5.2 代码实现
package a03.linkedList;
import java.util.Arrays;
public class CircleLinkedList {
//创建一个头结点
private PeopleNode head;
private int size;
//添加节点
public void add(PeopleNode people) {
//如果没有节点,直接加入
if (size == 0) {
head = people;
size++;
return;
}
//有节点,找到最后一个加入的节点
PeopleNode temp = head;
while (true) {
if (temp.next == head) {
break;
}
temp = temp.next;
}
temp.next = people;
people.next = head;
size++;
}
//删除节点
public void delete(int no) {
if (size == 0) {
System.out.println("链表为空");
return;
}
//找到待删除节点的前一个节点
PeopleNode temp = head;
boolean flag = false;
while (true) {
if (temp.next.no == no) {
flag = true;
break;
}
if (temp.next == head) {
break;
}
temp = temp.next;
}
//删除
if (flag) {
//如果链表只剩一个节点,头结点置为空
if (size == 1) {
head = null;
size--;
return;
}
//每次删除节点后,头结点改为下一个节点
head=temp.next.next;
temp.next = temp.next.next;
size--;
}
}
//改变指针头
public void changeHead( int no) {
if (head == null) {
System.out.println("链表为空");
}
//找到对应节点
PeopleNode temp = head;
boolean flag = false;
while (true) {
if (temp.no == no) {
flag = true;
break;
}
if (temp.next == head) {
break;
}
temp = temp.next;
}
//将其置为头结点
if (flag) {
head = temp;
}
}
//显示链表
public void show() {
if (head == null) {
System.out.println("链表为空");
return;
}
PeopleNode temp = head;
while (true) {
System.out.println(temp);
if (temp.next == head) {
break;
}
temp = temp.next;
}
}
//约瑟夫出圈
public int[] outers(int k, int m) {
int[] result = new int[size];
int index = 0;
changeHead(k);
while(true){
if(head == null){
break;
}
PeopleNode temp = head;
for(int i=1; i <=m; i++){
if(i == m){
result[index] = temp.no;
index++;
delete(temp.no);
}
temp = temp.next;
}
}
return result;
}
public static void main(String[] args) {
CircleLinkedList circleLinkedList = new CircleLinkedList();
for (int i = 1; i <= 25; i++) {
circleLinkedList.add(new PeopleNode(i));
}
System.out.println("----添加----");
circleLinkedList.show();
// circleLinkedList.delete(2);
// circleLinkedList.delete(1);
// circleLinkedList.delete(3);
// System.out.println("----删除----");
// circleLinkedList.show();
System.out.println(Arrays.toString(circleLinkedList.outers(5,7)));
}
}
class PeopleNode {
int no;
PeopleNode next = this;
public PeopleNode(int no) {
this.no = no;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
'}';
}
}
Q.E.D.
Comments | 0 条评论