算法笔记-双向链表与环形链表

1.双向链表的操作分析和实现

管理单向链表的缺点分析:

1) 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。

2) 单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到 temp,temp 是待删除节点的前一个节点(认真体会).

3) 分析了双向链表如何完成遍历,添加,修改和删除的思路

对上图的说明:

 

分析 双向链表的遍历,添加,修改,删除的操作思路===》代码实现

 

1) 遍历方和 单链表一样,只是可以向前,也可以向后查找

 

2) 添加(默认添加到双向链表的最后)

 

(1) 先找到双向链表的最后这个节点

 

(2) temp.next = newHeroNode

 

(3) newHeroNode.pre = temp;

 

3) 修改思路和 原来的单向链表一样.

 

4) 删除

 

(1) 因为是双向链表,因此,我们可以实现自我删除某个节点

 

(2) 直接找到要删除的这个节点,比如 temp

 

(3)   temp.pre.next = temp.next

 

(4) temp.next.pre = temp.pre;

java的LinkedList的源码就是使用的双向链表,今天看了一遍它的实现原理,有时间整理一下

 

2.单向环形链表介绍

Josephu(约瑟夫、约瑟夫环)    问题

Josephu    问题为设编号为 12 n n 个人围坐一圈约定编号为 k1<=k<=n)的人从 1 开始报数

m 的那个人出列它的下一位又从 1 开始报数数到 m 的那个人又出列依次类推直到所有人出列为止,由此产生一个出队编号的序列。

 

提示用一个不带头结点的循环链表来处理 Josephu  问题先构成一个有 n 个结点的单循环链表然后由 k 点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。

首先创建环形链表:

 

出圈:

初始化时helper指向5;first指向1

代码实现:

// 创建一个Boy类,表示一个节点
    class Boy {
        private int no;//编号
        private Boy next;

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

        public int getNo() {
            return no;
        }

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

        public Boy getNext() {
            return next;
        }

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

    }
//创建一个环形的单向链表
    class CircleSingleLinkedList {
        // 创建一个first节点,当前没有编号
        private Boy first = null;

        //构建一个环形链表
        public void addBoy(int nums) {
            // nums 做一个数据校验
            if (nums < 1) {
                System.out.println("nums的值不正确");
                return;
            }
            Boy cur = null;// 辅助指针,帮助构建环形链表
            for (int i = 1; i <= nums; i++) {
                Boy boy = new Boy(i);
                if (i == 1) {
                    first = boy;
                    first.setNext(first);//构成环
                    cur = first;
                } else {
                    cur.setNext(boy);
                    boy.setNext(first);
                    cur = boy;
                }
            }

        }

        // 遍历当前的环形链表
        public void show() {
            // 判断链表是否为空
            if (first == null) {
                System.out.println("没有任何小孩~~");
                return;
            }
            // 因为first不能动,因此我们仍然使用一个辅助指针完成遍历
            Boy cur = first;
            while (true) {
                System.out.printf("小孩的编号 %d \n", cur.getNo());
                if (cur.getNext() == first) {// 说明已经遍历完毕
                    break;
                }
                cur = cur.getNext(); // curBoy后移
            }

        }

        // 根据用户的输入,计算出小孩出圈的顺序

        /**
         * @param startNo  表示从第几个小孩开始数数
         * @param countNum 表示数几下
         * @param nums     表示最初有多少小孩在圈中
         */
        public void count(int startNo, int countNum, int nums) {
            // 先对数据进行校验
            if (first == null || startNo < 1 || startNo > nums) {
                System.out.println("参数输入有误, 请重新输入");
                return;
            }
            // 创建要给辅助指针,帮助完成小孩出圈
            Boy helper = first;
            // 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点
            while (true) {
                if (helper.getNext() == first) { // 说明helper指向最后小孩节点
                    break;
                }
                helper = helper.getNext();//helper 指向5; first指向1
            }
            //小孩报数前,先让 first 和  helper 移动 k - 1次
            for (int j = 0; j < startNo - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈
            //这里是一个循环操作,知道圈中只有一个节点
            while (true) {
                if (helper == first) { //说明圈中只有一个节点
                    break;
                }
                //让 first 和 helper 指针同时 的移动 countNum - 1
                for (int j = 0; j < countNum - 1; j++) {
                    first = first.getNext();
                    helper = helper.getNext();
                }
                //这时first指向的节点,就是要出圈的小孩节点
                System.out.printf("小孩%d出圈\n", first.getNo());
                //这时将first指向的小孩节点出圈
                first = first.getNext();//first向下移动一位
                helper.setNext(first); //

            }
            System.out.printf("最后留在圈中的小孩编号%d \n", first.getNo());

        }


    }

测试:

public class Josepfu {
    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);
        circleSingleLinkedList.show();
        circleSingleLinkedList.count(2,3,5);
    }

}

 


版权声明:本文为qq_39823114原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。