数据结构

342 阅读6分钟

数据结构

第一章 数组和队列

一. 稀疏数组

1.基本介绍:

当一个数组中大部分元素是0,或者是同一个值得数组时,可以用稀疏数组来保存该数组

2.处理方法:

1.记录数组一共有几行几列,有多少个不同得值

2.把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

例:

3.二维数组转稀疏数组的思路

1.遍历 原始的二维数组,得到有效的数据的个数sum
2.根据sum就可以创建稀疏数组sparseArr int[sum+1][3]
3.将二维数组的有效数据存入到稀疏数组中

4.稀疏数组转原始的二维数组

1.读取稀疏数组的第一行的,根据第一行的数据创建原始二维数组,比如chessArr=int[6][7]
2.再读取稀疏数组后几行的数据,并赋值给原始的二维数组
public class SparseArray {
    public static void main(String[] args) {
        //创建一个原始的二维数组
        //0:没有子 1:表述黑子  2:表示白字
        int chessArr[][] = new int[11][11];
        chessArr[1][2] = 1;
        chessArr[2][3] = 2;
        chessArr[4][5] = 2;
        //输出原始二维数组
        System.out.println("--------原始数组-------");
        printf(chessArr);

        //二维数组转稀疏数组
        int spareArr[][] = arrayToSpare(chessArr);
        //输出稀疏数组
        System.out.println("--------稀疏数组-------");
        printf(spareArr);

        //输出原始二维数组
        System.out.println("--------原始数组-------");
        int arr[][] = spareToArray(spareArr);
        printf(arr);
    }

    /*
     * 二维数组转稀疏数组
     */
    private static int[][] arrayToSpare(int chessArr[][]) {
        int sum = 0;
        //1.遍历 原始的二维数组,得到有效的数据的个数sum
        for (int[] row : chessArr) {
            for (int data : row) {
                if (0 != data) {
                    sum++;
                }
            }
        }
        //初始化稀疏数组
        int spareArr[][] = new int[sum + 1][3];
        //给稀疏数组赋值
        //获取行数
        spareArr[0][0] = chessArr.length;
        //获取列数
        spareArr[0][1] = chessArr[0].length;
        spareArr[0][2] = sum;
        //用于记录第几个非0数据
        int count = 0;
        for (int i = 0; i < chessArr.length; i++) {
            for (int j = 0; j < chessArr[i].length; j++) {
                if (0 != chessArr[i][j]) {
                    count++;
                    spareArr[count][0] = i;
                    spareArr[count][1] = j;
                    spareArr[count][2] = chessArr[i][j];
                }
            }
        }
        return spareArr;
    }

    /*
     * 稀疏数组转二维数组
     */
    private static int[][] spareToArray(int spareArr[][]) {
        //获取稀疏数组第一行
        //二维数组的行数
        int row =spareArr[0][0];
        //二维数组的列数
        int col =spareArr[0][1];
        //原始二维数组
        int array[][]= new int[row][col];
        for(int i = 1; i < spareArr.length; i++){
            //获取有数据的行
            int rowNum= spareArr[i][0];
            //获取有数据的列
            int colNum= spareArr[i][1];
            array[rowNum][colNum]=spareArr[i][2];
        }
        return array;
    }

    /*
     * 打印二维数组
     */
    private static void printf(int arr[][]) {
        for (int[] row : arr) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }
}
输出:
--------原始数组-------
0	0	0	0	0	0	0	0	0	0	0	
0	0	1	0	0	0	0	0	0	0	0	
0	0	0	2	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	2	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
--------稀疏数组-------
11	11	3	
1	2	1	
2	3	2	
4	5	2	
--------原始数组-------
0	0	0	0	0	0	0	0	0	0	0	
0	0	1	0	0	0	0	0	0	0	0	
0	0	0	2	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	2	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	

二.队列

1.队列介绍

队列是一个有序的列表,可以用数组和链表来实现,遵循先入先出的原则

2.数组模拟队列

1.队列的输出,输入分别从前后端来处理,因此需要两个变量front及rear记录队列的前后端,
  front会随着数据的输出而改变,rear随着数据的输入而改变
存队列:
	1.将尾指针往后移:rear+1  front==rear 空
	2.若尾指针rear小于队列的最大下标maxSize-1,存入数组,若rear==maxSize-1队列满

/*
 * 使用数组模拟队列
 */
class Array {
    //数组的最大容量
    private int maxSize;
    //对头的位置
    private int front;
    //队尾的位置
    private int rear;
    //该数组用于模拟队列
    private int[] arr;

    //创建队列的构造器
    public Array(int size) {
        maxSize = size;
        arr = new int[maxSize];
        front = -1; //指向队列的头部,front指向队列头的前一个位置
        rear = -1;//指向队列的尾部,rear指向队列尾的位置
    }

    //判断队列是否为满
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    //判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    //添加队列
    public void addQueue(int n) {
        //判断队列是否为满
        if (isFull()) {
            System.out.println("----满队列---");
            return;
        }
        //rear后移
        rear++;
        arr[rear] = n;
    }

    //出队列
    public int popQueue() {
        //判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        //front后移
        front++;
        return arr[front];
    }

    //显示队列的所有数据
    public void showQueue() {
        //判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        for (int i = front+1; i < maxSize; i++) {
            System.out.printf("arr[%d]=%d\n", i, arr[i]);
        }
    }

    //显示队列的头数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        return arr[front + 1];
    }
}

问题:

1.数组只能使用一次,没有达到复用的效果
2.将这个数组使用算法,改成一个环形的数组 %

3.数组模拟环形队列

思路:
1.front变量的含义做一个调整:front指向队列的第一个元素(arr[front] 队列的第一个元素)
2.rear变量的含义做一个调整:rear指向队列指向最后一个元素的后一个位置.(空出一个空间做出约定)
3.当队列满时: (rear+1)%maxSize=front
4.当队列为空时:front=rear
5.队列中有效的数据的个数 (rear+maxSize-front)%maxSize
class CircleArray {
    //数组的最大容量
    private int maxSize;
    //对头的位置
    private int front;
    //队尾的位置
    private int rear;
    //该数组用于模拟队列
    private int[] arr;

    //创建队列的构造器
    public CircleArray(int size) {
        maxSize = size;
        arr = new int[maxSize];
        front = 0; //指向队列的头部,front指向队列头的位置
        rear = 0;//指向队列的尾部,rear指向队列尾的后一个位置
    }

    //判断队列是否为满
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    //判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    //添加队列
    public void addQueue(int n) {
        //判断队列是否为满
        if (isFull()) {
            System.out.println("----满队列---");
            return;
        }
        //rear后移
        arr[rear] = n;
        rear = (rear + 1) % maxSize;
    }

    //出队列
    public int popQueue() {
        //判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        //front后移

        //1.先把front对应的值保留到一个临时变量
        //2.将front后移
        //3.将临时变量返回。
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }

    //显示队列的所有数据
    public void showQueue() {
        //判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        //思路:从front开始遍历,遍历多少个元素

        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }
    }

    //当前队列有效数据的个数
    public int size() {
        //rear = 1;
        //front = 0;
        //maxSize=3
        return (rear + maxSize - front) % maxSize;
    }

    //显示队列的头数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("----空队列---");
        }
        return arr[front];
    }
}

三.单链表

1.链表是以节点的方式来存储的,是链式存储的
2.每个节点包含data域,next域
3.链表的各个节点不一定是连续存放的
4.链表分带头节点的链表和没有头节点的链表

1.添加(创建)

1.先创建一个head头节点,表示单链表的头
2.后面每添加一个节点,就直接加到链表的最后
遍历:
1.通过一个辅助遍历,帮助遍历整个链表
package com.hyy.linkedList;

/**
 * @Author:
 * @Description:
 * @Date:2021/1/8
 * @Modified By:
 */
public class SingleLinkedListDemo {

    public static void main(String[] args) {
        SingleLinkedList singleLinkedList =new SingleLinkedList();
        HeroNode hero1 =new HeroNode(1,"宋江","及时雨");
        HeroNode hero2 =new HeroNode(2,"卢俊义","玉麒麟");
        HeroNode hero3 =new HeroNode(3,"吴用","智多星");
        HeroNode hero4 =new HeroNode(4,"林冲","豹子头");

        singleLinkedList.add(hero1);
        singleLinkedList.add(hero3);
        singleLinkedList.add(hero4);
        singleLinkedList.add(hero2);

        singleLinkedList.list();
    }
}

/*
 * 定义一个SingleLinkedList
 */
class SingleLinkedList{
    //先初始化一个头节点,头节点不要动
    private HeroNode head=new HeroNode(0,"","");

    //添加节点到单项链表
    //思路,当不考虑编号的顺序时,
    // 1.找到当前链表的最后节点
    // 2.将最后的节点的next指向新的节点
    public void add(HeroNode node){
        //因为不能动头节点,因此需要一个辅助节点
        HeroNode temp =head;
        //遍历链表
        while (true){
            if(temp.next==null){
                break;
            }
            //将链表后移
            temp=temp.next;
        }
        //当退出while循环时,temp指向了链表的最后
        temp.next = node;
    }
    //显示链表
    public void list(){
        if(head.next==null){
            return;
        }
        //因为不能动头节点,因此需要一个辅助节点
        HeroNode temp =head.next;
        while (true) {
            if(temp==null){
                break;
            }
            System.out.println(temp.toString());
            //将链表后移
            temp=temp.next;
        }
    }
}
/*
 * 定义一个HeroNode,每个HeroNode对象就是一个头节点
 */
class HeroNode{
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;

    //构造器
    public HeroNode(int hNo,String name,String nickName){
        this.no = hNo;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name=" + name +
                ", nickName=" + nickName +
                '}';
    }
    
    
------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
// 添加节点到单项链表
// 思路,当考虑编号的顺序时,
// 1.找到当前链表节点的下一个节点编号大于插入的编号的节点
// 2.将节点放在该节点的下一个位置上
public void addByOrder(HeroNode node){
    //因为不能动头节点,因此需要一个辅助节点
    HeroNode temp =head;

    boolean flag =false;
    //遍历链表
    while (true){
        if(temp.next==null){
            break;
        }
        //判断编号
        if(temp.next.no>node.no){
            break;
        }else if(temp.next.no==node.no){
            flag = true;
            break;
        }
        //将链表后移
        temp=temp.next;
    }
    if (flag) {
        //将node插入在temp的下一个节点中
        System.out.printf("编号%d存在,不能插入\n",node.no);
        return;
    }
    //当退出while循环时,temp指向了链表的最后
    node.next=temp.next;
    temp.next = node;
}

singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero4);
singleLinkedList.addByOrder(hero2);

------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}

2.修改单链表

//修改,根据编号,修改名称和昵称
public void update(HeroNode node){
    HeroNode temp = head.next;
    boolean flag = false;
    while (true) {
        if(temp==null){
            break;
        }
        //存在编号相同的
        if(temp.no==node.no){
            flag = true;
            break;
        }
        temp=temp.next;
    }
    if(flag){
        temp.name=node.name;
        temp.nickName=node.nickName;
    }else {
        System.out.printf("不存在编号为%d的英雄\n",node.no);
    }
}

HeroNode hero5 =new HeroNode(2,"李逵","黑旋风");
singleLinkedList.update(hero5);

------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
--------修改后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=李逵, nickName=黑旋风}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}

3.删除

//删除,删除指定no的英雄节点
public void delete(int no){
    HeroNode temp = head;
    //找到节点位置
    boolean flag =false;
    while (true) {
        if(temp.next == null){
            break;
        }
        if(temp.next.no == no){
            flag=true;
            break;
        }
        temp = temp.next;
    }
    if (flag) {
        temp.next=temp.next.next;
    }else {
        System.out.printf("不存在编号为%d的英雄\n",no);
    }
}

//删除
singleLinkedList.delete(4);
singleLinkedList.list();

HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
--------删除后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}

面试题

1.求单链表节点个数
//获取单链表节点个数(带头节点的链表,需要不统计头节点)
/**
*
* @param head 链表的头节点
* @return 有效节点的个数
*/
public static int getLength(HeroNode head){
    if(head.next==null){
        return 0;
    }
    int length=0;
    HeroNode cur =head.next;
    while (cur!=null){
        length++;
        cur = cur.next;
    }
    return length;
}
2.查找链表倒数第k个节点
//查找链表倒数第k个节点
public static HeroNode getNode(int k,HeroNode head){
    int length = getLength(head);
    if(length<k){
        System.out.printf("不存在倒数第%k个节点",k);
    }
    int n=length-k+1;
    HeroNode temp =head;
    for (int i = 0; i < n; i++) {
        temp=temp.next;
    }
    return temp;
}

HeroNode node = getNode(2, singleLinkedList.getHead());

------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
有效节点个数:5
HeroNode{no=4, name=林冲, nickName=豹子头}
3.单链表的反转
//反转链表
public static void reverse(HeroNode head){
    //反转链表
    HeroNode reverse = new HeroNode(0,"","");
    //循环的temp节点
    HeroNode temp = head.next;
    //存储的节点
    HeroNode t=null;
    while (temp !=null){
        t = temp;
        temp=temp.next;
        t.next=reverse.next;
        reverse.next=t;
    }
    //把头节点交换
    head.next=reverse.next;
}

reverse(singleLinkedList.getHead());
singleLinkedList.list();

HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
--------反转后的链表---------
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=1, name=宋江, nickName=及时雨}
4.从尾到头打印单链表
思路:利用栈这个数据结构
//从尾到头打印链表
public static void reversePrint(HeroNode head) {
    Stack<HeroNode> stack = new Stack<>();

    HeroNode temp = head.next;
    while (temp != null) {
        stack.push(temp);
        temp = temp.next;
    }
    while (stack.size() > 0) {
        HeroNode pop = stack.pop();
        System.out.println(pop);
    }
}

System.out.println("--------逆序打印链表---------");
reversePrint(singleLinkedList.getHead());

------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=5, name=李逵, nickName=黑旋风}
--------逆序打印链表---------
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=1, name=宋江, nickName=及时雨}
5.合并两个有序链表
//合并两个有序单链表
public static void merge(HeroNode head1,HeroNode head2){
    HeroNode temp1 = head1.next;
    HeroNode temp2 = head2.next;

    HeroNode reverse =new HeroNode(0,"","");

    //要遍历的节点
    HeroNode t =null;
    //新节点的临时头节点
    HeroNode p = reverse;
    while (temp1 != null || temp2 != null) {
        //循环到第一个为空时,只遍历第二个链表
        if(temp1 ==null){
            reverse.next=temp2;
            temp2= temp2.next;
            reverse=reverse.next;
            //循环到第二个为空时,只遍历第一个链表
        }else if(temp2 ==null){
            reverse.next=temp1;
            temp1= temp1.next;
            reverse=reverse.next;
        }else if(temp1.no<temp2.no){
            t = temp1;
            temp1=temp1.next;
            reverse.next=t;
            reverse=reverse.next;
        }else{
            t = temp2;
            temp2=temp2.next;
            reverse.next=t;
            reverse=reverse.next;
        }
    }
    head1.next = p.next;
}

System.out.println("--------合并打印链表---------");
merge(singleLinkedList.getHead(),singleLinkedList2.getHead());
singleLinkedList.list();

--------链表1---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=7, name=卢俊义7, nickName=玉麒麟}
HeroNode{no=8, name=吴用8, nickName=智多星}
HeroNode{no=10, name=李逵10, nickName=黑旋风}
--------链表2---------
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=6, name=宋江6, nickName=及时雨}
HeroNode{no=9, name=林冲9, nickName=豹子头}
--------合并打印链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=6, name=宋江6, nickName=及时雨}
HeroNode{no=7, name=卢俊义7, nickName=玉麒麟}
HeroNode{no=8, name=吴用8, nickName=智多星}
HeroNode{no=9, name=林冲9, nickName=豹子头}
HeroNode{no=10, name=李逵10, nickName=黑旋风}

四.双链表

1.单链表的缺点

1.查找的方向只能是一个方向。双向链表可以向前或者向后查找。
2.单向链表不能自我删除,需要靠辅助节点。而双向链表,可以自我删除

2.添加

1.遍历方法和单链表一样,只是可以向前,也可以向后查找
2.添加(默认添加到双向链表的最后)
temp.next = new Node();
new Node.pre = temp;
package com.hyy.linkedList;

/**
 * @Author:
 * @Description:
 * @Date:2021/1/12
 * @Modified By:
 */
public class DoubleLinkedListDemo {

    public static void main(String[] args) {
        DoubleLinkedList linkedList = new DoubleLinkedList();
        HeroNode2 hero1 =new HeroNode2(1,"宋江","及时雨");
        HeroNode2 hero2 =new HeroNode2(2,"卢俊义","玉麒麟");
        HeroNode2 hero3 =new HeroNode2(3,"吴用","智多星");
        HeroNode2 hero4 =new HeroNode2(4,"林冲","豹子头");
        HeroNode2 hero5 =new HeroNode2(5,"李逵","黑旋风");

        linkedList.add(hero1);
        linkedList.add(hero3);
        linkedList.add(hero4);
        linkedList.add(hero5);
        linkedList.add(hero2);

        linkedList.list();
    }

}

class DoubleLinkedList{

    //先初始化一个头节点,头节点不要动
    private HeroNode2 head=new HeroNode2(0,"","");

    public HeroNode2 getHead() {
        return head;
    }

    //遍历双向链表的方法
    public void list(){
        if (head.next == null) {
            return;
        }
        HeroNode2 temp = head.next;
        while (true){
            if (temp==null){
                break;
            }
            System.out.println(temp.toString());
            temp = temp.next;
        }
    }

    //添加
    public void add(HeroNode2 node){
        HeroNode2 temp = head;
        while (temp.next!=null){
           temp=temp.next;
        }
        //形成双向链表
        temp.next=node;
        node.pre=temp;
    }
}

/*
 * 定义一个HeroNode,每个HeroNode对象就是一个头节点
 */
class HeroNode2{
    public int no;
    public String name;
    public String nickName;
    public HeroNode2 next;
    public HeroNode2 pre;

    //构造器
    public HeroNode2(int hNo,String name,String nickName){
        this.no = hNo;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name=" + name +
                ", nickName=" + nickName +
                '}';
    }
}

----------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}

3.删除

双向链表,可以实现自我删除
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
//删除
public void delete(int no){
    HeroNode2 temp = head.next;

    boolean flag = false;
    while (true) {
        if (temp== null) {
            break;
        }
        if(temp.no==no){
            flag =true;
            break;
        }
        temp=temp.next;
    }
    if (flag) {
        //删除的节点不是最后一个节点
        if(temp.next!=null){
            temp.next.pre=temp.pre;
        }
        temp.pre.next=temp.next;
    }else {
        System.out.printf("不存在编号为%d的英雄\n",no);
    }
}

//删除
linkedList.delete(4);
linkedList.list();

HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
--------删除后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}

4.双向链表按顺序添加

//按顺序添加
public void addByOrder(HeroNode2 node){
    HeroNode2 temp = head;
    boolean flag=false;
    while (true){
        if(temp.next==null){
            break;
        }
        if(temp.no>node.no){
            break;
        }
        if (temp.no==node.no){
            flag=true;
            break;
        }
        temp=temp.next;
    }
    if (flag) {
        System.out.printf("编号%d存在,不能插入\n",node.no);
        return;
    }else {
        if(temp.next==null){
            temp.next=node;
            node.pre=temp;
            return;
        }
        //顺序添加4条线
        temp.pre.next=node;
        node.pre=temp.pre;
        temp.pre=node;
        node.next=temp;
    }
}

linkedList.addByOrder(hero1);
linkedList.addByOrder(hero3);
linkedList.addByOrder(hero4);
linkedList.addByOrder(hero5);
linkedList.addByOrder(hero2);

linkedList.list();

------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}

五.单向环形链表

1.环形链表生成

public class Joseph {
    public static void main(String[] args) {

        CircleLinkedList joseph = new CircleLinkedList();
        joseph.add(7);
        joseph.list();
    }

}

class CircleLinkedList{
    //创建一个first节点
    private Boy first =null;

    //添加小孩节点,构建环形链表
    public void add(int nums){
        if(nums<1){
            System.out.println("nums的值不正确");
            return;
        }
        Boy temp =null;
        for (int i = 1; i <= nums; i++) {
            //根据编号创建节点
            Boy b=new Boy(i);
            if(i==1){
                first=b;
                b.setNext(first);//构成环
                temp=first;
            }else{
                temp.setNext(b);
                b.setNext(first);
                temp=temp.getNext();
            }
        }
    }

    public void list(){
        if(first==null){
            System.out.println("-----空环形链表------");
            return;
        }
        Boy temp = first;
        while (true){
            System.out.printf("小孩的编号为:%d\n",temp.getNo());
            if(temp.getNext()==first){
                break;
            }
            temp = temp.getNext();
        }
    }
}

//创建一个Boy类,表示一个节点
class Boy{
    private int no;
    private Boy next;//指向下一个节点

    public Boy(int no){
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }
}

-------------------
小孩的编号为:1
小孩的编号为:2
小孩的编号为:3
小孩的编号为:4
小孩的编号为:5
小孩的编号为:6
小孩的编号为:7
约瑟夫问题:
1.创建一个辅助指针(变量)temp,事先指向最后一个节点
2.当小孩报数时,让temp指针移动m-1次。
public  void joseph(int k,int m){
    //辅助节点放在最后一个节点上
    Boy temp = first;
    while (true){
        if(temp.getNext()==first){
            break;
        }
        temp=temp.getNext();
    }
    //移动k-次(第几个小孩报数)
    for (int i = 1; i < k; i++) {
        temp=temp.getNext();
    }
    while (true){
        if(first.getNext()==first){
            System.out.printf("小孩的编号为:%d\n",first.getNo());
            break;
        }
        for (int i = 1; i < m; i++) {
            temp = temp.getNext();
        }
        System.out.printf("小孩的编号为:%d\n",temp.getNext().getNo());
        temp.setNext(temp.getNext().getNext());
        //移动first
        first=temp;
    }
}

-------------
小孩的编号为:1
小孩的编号为:2
小孩的编号为:3
小孩的编号为:4
小孩的编号为:5
----------约瑟夫---------
小孩的编号为:2
小孩的编号为:4
小孩的编号为:1
小孩的编号为:5
小孩的编号为:3