参考自视频:《尚硅谷Java数据结构和Java算法》,并对代码进行详细解释
一、单链表
public class SingleLinkedListDemo {
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,"林冲","豹子头");
//创建链表对象
SingleLinkedList single = new SingleLinkedList();
//加入结点
single.add(hero1);
single.add(hero2);
single.add(hero3);
single.add(hero4);
//显示链表
single.list();
}
}
//2.创建一个类来管理链表
class SingleLinkedList{
//先初始化一个头结点,设为私有,无法被其他类调用,不存放数据
private HeroNode head = new HeroNode(0,"","");
//2.1添加结点的方法
public void add(HeroNode heronode){
//由于head不能动,所以需要一个辅助结点temp来遍历
HeroNode temp = head;
//遍历链表,找到最后一个结点 !
while(true) {
if(temp.next==null) {
break;//说明已经指向了链表的最后null,所以跳出循环直接指向插入操作
}
temp = temp.next;//没有到最后一个结点,temp结点后移
}
temp.next = heronode;//让最后一个结点的next域指向新结点
}
//2.2显示链表
public void list() {
//先判断链表是否为空,为空即头结点指向的第一个节点为空结点
if(head.next==null) {
System.out.println("结点为空");
return;//结束方法
}
HeroNode temp = head;//head不能动,用辅助结点来遍历
//通过遍历整个链表来输出结点对象
while(true) {
if(temp.next == null) {
break;//跳出循环
}
temp = temp.next;//temp后移
//输出结点信息
System.out.println(temp);//输出辅助结点对象,之前写过了toString方法
}
}
}
//1.写一个结点类,由内容和指针组成,在类中若以以下结构定义,会直接默认结点类
class HeroNode{
//内容data
public int no;
public String name;
public String nickname;
//next是HeroNode结点对象的对象(属性)
public HeroNode next;//next是指向下一个结点的指针对象,存放下一结点地址域
//构造器,用于构造对象,将对象的实例变量初始化,即将类中成员变量传给对象
public HeroNode(int no,String name,String nickname) {
this.no=no;//this对象的成员变量等于传进来的类中的成员变量
this.name=name;
this.nickname=nickname;
}
//为输出想要的对象格式,重写toString方法
@Override
public String toString() {
return "HeroNode[no="+no+",name="+name+",nickname="+nickname+"]";
}
}
二、双向链表
——每个结点都带有两个指针的
添加数据:
temp.next = new Node(data);
Node.pre = temp;
删除数据:
1.可以实现自我删除temp;
2. temp.pre.next=temp.next; //temp的前一个结点指向了temp的后一个结点
3.temp.next.pre = temp.pre//因为是双向的,所以还需要temp的后一个结点指向temp的前一个结点
【双链表的操作】:
package queue;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
Node node1 = new Node(10);
Node node2 = new Node(20);
Node node3 = new Node(30);
Node node4 = new Node(40);
Node node5 = new Node(50);
DoubleLinkedList dl = new DoubleLinkedList();
dl.add(node1);
dl.add(node2);
dl.add(node3);
dl.add(node4);
dl.add(node5);
dl.list();
System.out.println("删除结点后:");
dl.delectNode(30);
dl.list();
}
}
//2.管理结点的链表类
class DoubleLinkedList{
private Node head = new Node(0);
//添加结点
public void add(Node node) {
Node temp = head;
//找出末端的结点
while(true) {
if(temp.next==null) {
break;
}
temp = temp.next;
}
//添加结点,形成双向链表
temp.next = node;//让最后一个结点指向新结点
node.pre = temp;//双向连接
}
//删除结点
public void delectNode(int data) {
if(head.next==null) {
System.out.println("链表为空!");
return;//结束方法
}
Node temp = head;//如果是单链表,需要找到待删除结点的前一个,这里找到其本身即可
boolean flag = false;
while(true) {
if(temp == null) {
break;//已到链表末端
}
if(temp.data == data) {
flag=true;
break;
}
temp = temp.next;
}
if(flag) {//已经找到该结点
temp.pre.next=temp.next;//这句也可以用于删除最后一个结点
//如果是最后一个结点就不需要执行下一句
if(temp.next!=null) {
temp.next.pre=temp.pre;
}
}
}
//显示链表
public void list() {
if(head.next==null) {
System.out.println("结点为空!");
return;
}
Node temp = head;
while(true) {
if(temp.next==null) {
break;
}
temp=temp.next;
System.out.println(temp);
}
}
}
//1.双链表的结点类
class Node{
public int data;
public Node next;
public Node pre;
//构造器
public Node(int data) {
this.data = data;
}
@Override
public String toString() {
return "Node=["+data+"]";
}
}
三、单向循环链表
问题的引出——约瑟夫问题(Josephu):
问题描述:编号为1-n的小孩围成一圈,约定编号为k的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人都出列,由此产生一个出队编号的序列。
思路:
可用一个不带头结点的循环链表来处理:先构成一个有n个节点的循环单链表,然后由k结点起从1开始计数,计到m时,对于的结点从链表中删除,然后从被删除结点的下一个结点又从1开始计数,知道最后一个结点被删除。
【手撕链表操作】
public class Main{
public static void main(String[] args){
Node node0 = new Node(0);
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node head = new Node(0);
node0.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
list(node0);
delect(head,node0);
list(node0);
}
/*
public void add(Node node){
Node temp = head;
while(true){
if(temp.next==null){
break;
}
temp=temp.next;
}
temp.next = node;
}
*/
public static void delect(Node head,Node delNode)//头结点,删除的结点
{
if(head==null || delNode==null)
{
return;
}
Node temp = head;
if(delNode.next != null)//要删除的结点不是尾结点
{
delNode.value=delNode.next.value;//将要删除的结点值替换成下一个结点的值
delNode.next = delNode.next.next;//将要删除的结点的指针替换成下个结点指针,因为要替换它的是下个结点
}
else if(delNode.next==null)//如果要删除的结点是尾结点
{
while(temp.next!=delNode)//用临时指针从头结点开始遍历,到达尾结点前一个结点,将指向null
{
temp = temp.next;
}
temp.next=null;
}
}
public static void list(Node node)
{
while(node != null)
{
System.out.println(node.value);
node = node.next;
}
}
}
class Node
{
int value;
Node next;
public Node(int value){
this.value = value;
}
}