数据结构与算法 双向链表

在单链表的基础上加上一个pre节点,用来保存上一个节点

[pre-Data-next] 看起来这种感觉qwq

下面是代码实现

public class DoubleLinkedList {
    public NewHeroNode getNewHeroNode() {
        return newHeroNode;
    }

    //拿这个做头结点啦
    private final NewHeroNode newHeroNode = new NewHeroNode(null, null, 0);

    /**
     * 双向链表-添加节点-尾插法
     *
     * @param heroNode
     */
    public void addRear(NewHeroNode heroNode) {
        NewHeroNode cur = getNewHeroNode();
        //循环遍历找到最后一个节点
        while (cur.next != null) {
            cur = cur.next;
        }
        heroNode.pre = cur;//新增节点的pre指向头结点
        cur.next = heroNode;//头结点的next指向新增节点
    }

    /**
     * 修改链表某个节点 根据no进行修改 修改方式和单链表几乎一样
     *
     * @param newHeroNode
     * @return
     */
    public boolean update(NewHeroNode newHeroNode) {
        if (getNewHeroNode().next == null) {
            return false;
        }
        //找到需要修改的节点
        //遍历链表
        NewHeroNode cur = getNewHeroNode().next;
        boolean flag = false;
        while (cur != null) {
            if (cur.getNo() == newHeroNode.getNo()) {
                cur.setName(newHeroNode.getName());
                cur.setNickName(newHeroNode.getNickName());
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    public NewHeroNode delete(int No) {
        NewHeroNode cur = getNewHeroNode().next;  //获取链表的第一个节点
        while (cur != null) {
            if (cur.getNo() == No && cur.next != null) {
                //下面就是删除节点
                cur.pre.next = cur.next;
                cur.next.pre = cur.pre;
                return cur;
            }else if (cur.getNo() == No && cur.next == null){
                //如果是删除最后一个节点
                cur.pre.next = cur.next;
                return cur;
            }
            cur = cur.next;
        }
        System.out.println("删除失败");
        return null;
    }

    /**
     * 遍历双向链表链表
     */
    public void list() {
        //判断链表是否为空
        if (newHeroNode.next == null) {
            System.out.println("链表为空");
            return;
        }
        NewHeroNode cur = newHeroNode.next;
        while (cur != null) {
            System.out.println(cur.toString());
            cur = cur.next;
        }
    }

}

约瑟夫问题

讲一个比较有意思的故事:约瑟夫是犹太军队的一个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余人,他们都是宁死不屈的人,所以不愿投降做叛徒。一群人表决说要死,所以用一种策略来先后kill所有人。
于是约瑟夫建议:每次由其他两人一起kill一个人,而被kill的人的先后顺序是由抽签决定的,约瑟夫有预谋地抽到了最后一签,在kill了除了他和剩余那个人之外的最后一人,他劝服了另外一个没死的人投降了罗马。

我们这个规则是这么定的:
在一间房间总共有n个人(下标0~n-1),只能有最后一个人活命。

按照如下规则去排除人:

    所有人围成一圈
    顺时针报数,每次报到q的人将被排除掉
    被排除掉的人将从房间内被移走
    然后从被kill掉的下一个人重新报数,继续报q,再清除,直到剩余一人
 

/**
 * 约瑟夫环
 * 首先需要一个单向环形链表
 */
public class Josephu {
    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList =
                new CircleSingleLinkedList();
        circleSingleLinkedList.add(5);
        circleSingleLinkedList.showBoy();
        circleSingleLinkedList.countBoy(1,2,5);
    }
}

class CircleSingleLinkedList {
    private Boy first = new Boy(-1);//用来指向第一个节点

    /**
     * 约瑟夫环实现
     *
     * @param startNo  表示从第几个小孩开始数数
     * @param countNum 表示数几下
     * @param num      表示最初有多少小孩在圈中
     */
    public void countBoy(int startNo, int countNum, int num) {
        //先进行数据校验
        if (first == null || startNo < 1 || startNo > num) {
            System.out.println("参数非法!");
            return;
        }
        //创建一个辅助指针,帮助完成小孩出圈
        Boy helper = first;
        //让helper指向最后一个节点
        while (helper.getNext() != first) {
            helper = helper.getNext();
        }
        //因为startNo不一定每次都是在1,也有可能在2可能在4。
        // 之前的工作是为了让first指针指向第一个节点,helper指向最后一个节点。
        // 假设startNo是2,那fist和helper就要移动一次,所以要让小孩移动startNo-1次
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //小孩报数时,让first和helper指针同时移动countNum-1次(因为当前被指到的节点也要喊一个数嘛),然后出圈
        //在这里不停的循环直到只剩下一个节点
        while (true){
            if (helper == first){
                break;
            }
            for (int i = 0; i < countNum - 1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //循环完后first指向的节点就是要出圈的节点
            System.out.printf("\n小孩%d出圈啦",first.getNo());
            //下面就是出圈操作
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.println("\n最后留在圈中的小孩:"+first.getNo());
    }


    /**
     * //添加新节点,构建成一个环形链表 一次性加多个Boy
     *
     * @param nums
     */
    public void add(int 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; //因为first要保持在第一个节点上,所以接下来让cur代劳
            } else {
                //增加第一个节点之后的所有节点都这样做
                cur.setNext(boy); //当前节点的下一个节点设为新增的节点
                boy.setNext(first);//因为新增是在链表尾部,所以新增的节点也是在尾部,让它指向first即可完成循环
                cur = boy; //cur新增的节点,即尾部
            }
        }
    }

    /**
     * 遍历环形链表
     */
    public void showBoy() {
        //校验
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        //设置一个辅助指针
        Boy cur = first;
        do {
            System.out.println("\n小孩的编号为:" + cur.getNo());
            cur = cur.getNext();
        }
        while (cur.getNext() != first);
        System.out.println("\n小孩的编号为:" + cur.getNo());
    }
}

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;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值