链表Linked List(双向,环形、背包)

链表

链表是有序的列表,但是它在内存中是存储如下:

在这里插入图片描述
小结:

  1. 链表是以节点的方式来存储,是链式存储每个节点包含 data 域(数据), next 域:指向下一个节点.

  2. 如图:发现链表的各个节点不一定是连续存储.

  3. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

添加(创建):

1.先创建一个head 头节点, 作用就是表示单链表的头

2.后面我们每添加一个节点,就直接加入到 链表的最后

遍历

通过一个辅助变量(temp)遍历,帮助遍历整个链表

插入

1.按照编号顺序添加

2.通过一个辅助变量(temp),找到插入数据的前一个节点。

3.temp.getNext(新节点)

4.将新节点.setNext(temp.getNext())

删除

1.辅助变量(temp),找到要删除的节点的前一个结点

2.将前一个结点的next指向要删除节点的next

反转

  1. 头插法
package com.linkedlist;

/**
 * @author ming
 * @create 2020-02-09 15:53
 */
public class linkedListDemo {
    public static void main(String[] args) {
        //创建对象
        HeroNode node = new HeroNode(1, "婉君", "紫霞");
        HeroNode node1 = new HeroNode(2, "家明", "至尊宝");
        HeroNode node2 = new HeroNode(3, "牛魔王", "酋长");

        HeroNode node3 = new HeroNode(1, "天蓬元帅", "八戒");
        //添加数据
        SingLinkedList sll = new SingLinkedList();

//        sll.add(node);
//        sll.add(node2);
//        sll.add(node1);
        //HeroNode{no=1, name='婉君', nicklname='紫霞'}
        //HeroNode{no=3, name='牛魔王', nicklname='酋长'}
        //HeroNode{no=2, name='家明', nicklname='至尊宝'}

        //按编号顺序加入
        sll.addByOrder(node);
        sll.addByOrder(node2);
        sll.addByOrder(node1);
//        sll.addByOrder(node3);

        //修改数据
        HeroNode newHeroNode = new HeroNode(3, "李华", "读英语");
        sll.update(newHeroNode);

        //删除指定编号数据
        int i = 2;
//        sll.delete(i);

        //遍历链表
        sll.list();

        //单链表反转
        SingLinkedList.reverseList(sll.getHead());
        System.out.println("\n 反转:");
        sll.list();

    }
}

//定义SingleLinkedList管理数据
class SingLinkedList {
    //初始化头节点
    private HeroNode head = new HeroNode(0, "", "");

    //添加节点到单链表尾部
    //找到当前接链表的最后一个节点,将最后一个节点的next指向添加节点的地址
    public void add(HeroNode heroNode) {
        //head节点
        HeroNode temp = head;
        //遍历链表,找最后节点
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            temp = temp.getNext();
        }
        temp.setNext(heroNode);
    }

    //将数据插入指定位置,如果位置上已有数据,添加失败
    public void addByOrder(HeroNode heroNode) {
        //创建辅助变量,找到插入数据的前一个节点
        HeroNode temp = head;
        //标志添加的编号是否存在,默认false
        boolean flag = false;
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            //位置已找到
            if (temp.getNext().getNo() > heroNode.getNo()) {
                break;
            }
            //需添加数据的编号已存在
            if (temp.getNext().getNo() == heroNode.getNo()) {
                flag = true;
                break;
            }
            //后移temp
            temp = temp.getNext();
        }
        //判断flag值
        if (flag) {
            System.out.printf("数据编号 %d 已存在 \n", heroNode.getNo());
        } else {
            //插入数据
            heroNode.setNext(temp.getNext());
            temp.setNext(heroNode);
        }
    }

    //修改节点,根据no来修改,no编号不能改
    public void update(HeroNode newHeroNode) {
        HeroNode temp = head.getNext();
        boolean flag = false;
        while (true) {
            if (temp == null) {
                break;
            }
            if (newHeroNode.getNo() == temp.getNo()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            temp.setName(newHeroNode.getName());
            temp.setNicklname(newHeroNode.getNicklname());
        } else {
            System.out.printf("未找到编号为 %d 的数据\n", newHeroNode.getNo());
        }

    }

    //删除节点,根据编号no删除
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false;
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            if (temp.getNext().getNo() == no) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }

        if (flag) {
            temp.setNext(temp.getNext().getNext());
        } else {
            System.out.printf("未找到编号为 %d 的数据\n", no);
        }
    }

    //单链表反转
    public static void reverseList(HeroNode head) {
        //但链表为空或只有一个节点
        if (head.getNext() == null || head.getNext().getNext() == null) {
            return;
        }
        //定义一个辅助变量
        HeroNode cur = head.getNext();
        //指向当前结点的下一个节点
        HeroNode next = null;
        HeroNode reverseHead = new HeroNode(0, "", "");
        //遍历原来的节点,头插法
        if (head.getNext().getNext() == null) {
            return;
        } else {
            while (cur != null) {
                //辅助变量向下移
                next = cur.getNext();
                //将头节点setNext()赋给将要插入的节点的getNext()
                cur.setNext(reverseHead.getNext());
                //将要插入的节点赋给头结点的getNext()
                reverseHead.setNext(cur);
                //
                cur = next;
            }
            //将原来的头节点的setNext()加入反转头结点的getNext
            head.setNext(reverseHead.getNext());
        }
    }

    //遍历链表
    public void list() {
        if (head.getNext() == null) {
            System.out.println("链表为空");
            return;
        }

        //辅助变量
        HeroNode temp = head.getNext();
        while (true) {
            //判断是否到链表最后
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            //将temp后移
            temp = temp.getNext();
        }
    }

    public HeroNode getHead() {
        return head;
    }

    public void setHead(HeroNode head) {
        this.head = head;
    }
}


//定义一个HeroNode,每一个HeroNode就只一个节点
class HeroNode {
    private int no;
    private String name;
    private String nicklname;
    private HeroNode next;

    public HeroNode() {
    }

    public HeroNode getNext() {
        return next;
    }

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

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNicklname() {
        return nicklname;
    }

    public void setNicklname(String nicklname) {
        this.nicklname = nicklname;
    }

    public HeroNode(int no, String name, String nicklname) {
        this.no = no;
        this.name = name;
        this.nicklname = nicklname;
    }

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

双向链表

package com.linkedlist;

/**
 * @author ming
 * @create 2020-02-12 9:00
 */
public class doubleLinkedListDemo {
    public static void main(String[] args) {
        //创建对象
        HeroNode2 node = new HeroNode2(1, "婉君", "紫霞");
        HeroNode2 node1 = new HeroNode2(2, "家明", "至尊宝");
        HeroNode2 node2 = new HeroNode2(3, "牛魔王", "酋长");

//        添加数据
        doubleLinkedList dll = new doubleLinkedList();
//        dll.add(node);
//        dll.add(node2);
//        dll.add(node1);
        dll.addByOrder(node2);
        dll.addByOrder(node1);
        dll.addByOrder(node);
//        HeroNode2 node3 = new HeroNode2(1, "天蓬元帅", "八戒");
//        dll.addByOrder(node3);

        //遍历
        dll.list();
        //修改
        HeroNode2 node3 = new HeroNode2(1, "天蓬元帅", "八戒");
        dll.update(node3);
        //删除
        dll.delete(2);

        //遍历
        System.out.println("");
        dll.list();
    }
}

//创建双线链表类
class doubleLinkedList {
    //初始化头结点
    private HeroNode2 head = new HeroNode2(0);

    public HeroNode2 getHead() {
        return head;
    }

    public void setHead(HeroNode2 head) {
        this.head = head;
    }

    //遍历双向链表
    public void list() {
        if (head.getNext() == null) {
            System.out.println("链表为空");
            return;
        }

        //辅助变量
        HeroNode2 temp = head.getNext();
        while (true) {
            //判断是否到链表最后
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            //将temp后移
            temp = temp.getNext();
        }
    }

    //添加节点到双链表尾部
    //找到当前接链表的最后一个节点,将最后一个节点的next指向添加节点的地址
    public void add(HeroNode2 heroNode2) {
        //head节点
        HeroNode2 temp = head;
        //遍历链表,找最后节点
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            temp = temp.getNext();
        }
        temp.setNext(heroNode2);
        heroNode2.setPre(temp);
    }

    //修改节点
    public void update(HeroNode2 newHeroNode2) {
        HeroNode2 temp = head.getNext();
        boolean flag = false;
        while (true) {
            if (temp == null) {
                break;
            }
            if (newHeroNode2.getNo() == temp.getNo()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            temp.setName(newHeroNode2.getName());
            temp.setNickname(newHeroNode2.getNickname());
        } else {
            System.out.printf("未找到编号为 %d 的数据\n", newHeroNode2.getNo());
        }

    }

    //删除节点
    public void delete(int no) {
        HeroNode2 temp = head.getNext();
        boolean flag = false;
        while (true) {
            //到节点的最后
            if (temp == null) {
                break;
            }
            if (temp.getNo() == no) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }

        if (flag) {
            temp.getPre().setNext(temp.getNext());
            //防止出现空指针异常
            if (temp.getNext() != null) {
                temp.getNext().setPre(temp.getPre());
            }
        } else {
            System.out.printf("未找到编号为 %d 的数据\n", no);
        }
    }

    //将数据插入指定位置,如果位置上已有数据,添加失败
    public void addByOrder(HeroNode2 heroNode2) {
        //创建辅助变量,找到插入数据的前一个节点
        HeroNode2 temp = head;
        //标志添加的编号是否存在,默认false
        boolean flag = false;
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            //位置已找到
            if (temp.getNext().getNo() > heroNode2.getNo()) {
                break;
            }
            //需添加数据的编号已存在
            if (temp.getNext().getNo() == heroNode2.getNo()) {
                flag = true;
                break;
            }
            //后移temp
            temp = temp.getNext();
        }
        //判断flag值
        if (flag) {
            System.out.printf("数据编号 %d 已存在 \n", heroNode2.getNo());
        } else {
            //插入数据
            //顺序:此时应该先将插入的数据与他插入位置的后面的数据相连,再将插入的数据与他插入位置的前面的数据相连
            if (temp.getNext() != null) {
                temp.getNext().setPre(heroNode2);
                heroNode2.setNext(temp.getNext());
            }
            temp.setNext(heroNode2);
            heroNode2.setPre(temp);

//            temp.setNext(heroNode2);
//            heroNode2.setPre(temp);
//            if (temp.getNext() != null) {
//                temp.getNext().setPre(heroNode2);
//                heroNode2.setNext(temp.getNext());
//            }

        }
    }
}

//定义HeroNode2
class HeroNode2 {
    private int no;
    private String name;
    private String nickname;
    private HeroNode2 next;
    private HeroNode2 pre;

    public HeroNode2() {
    }

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

    public HeroNode2(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    public HeroNode2 getNext() {
        return next;
    }

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

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public HeroNode2 getPre() {
        return pre;
    }

    public void setPre(HeroNode2 pre) {
        this.pre = pre;
    }

    @Override
    public String toString() {
        return "HeroNode2{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + "'" +
                '}';
    }
}

单向环形链表

约瑟夫问题:设编号为1,2,… n的n个人围坐一圈,约定编号k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

构建一个单向的环形链表思路:

  1. 先创建第一个节点, 让 first 指向第一节点,并形成环形

  2. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.

遍历环形链表

  1. 先让一个辅助指针(变量) cur,指向first节点

  2. 然后通过一个while循环遍历(先将尾连头,再将前连后)该环形链表即可 cur == first 结束

例:n = 5 , 即有5个人;k = 1, 从第1个人开始报数(第一个人本身数一下);m = 2, 数2下

1、需求创建一个辅助指针(变量) help , 事先应该指向环形链表的第k-1个节点.

补充: 小孩报数前,先让 first 和 help 移动 k - 1次

2、当小孩报数时,让first 和 help 指针同时 的移动 m - 1 次

3、这时就可以将first 指向的小孩节点 出圈

first = first .getNext();

help.getNext() = first ;

原来first 指向的节点就没有任何引用,就会被回收

出圈的顺序:2->4->1->5->3

在这里插入图片描述

package com.linkedlist;

/**
 * @author ming
 * @create 2020-02-12 14:19
 */
public class ringLinkedList {
    public static void main(String[] args) {
        RingSingLinkedList rll = new RingSingLinkedList();
        rll.addBoy(5);
        rll.list();

        rll.countBoy(1, 2, 5);
    }
}

//环形单向链表
class RingSingLinkedList {
    //定义first为第一个节点
    private Boy first = null;

    //添加数据
    public void addBoy(int nums) {
        //nums数据校验
        if (nums < 1) {
            System.out.println("数据不正确");
            return;
        }
        //辅助变量
        Boy cur = null;
        //创建环形链表
        for (int i = 1; i <= nums; i++) {
            //根据编号,创建数据
            Boy boy = new Boy(i);
            //第一个数据
            if (i == 1) {
                first = boy;
                first.setNext(boy);
                cur = boy;
            } else {
                //尾连头,顺序:先将尾连头
                boy.setNext(cur.getNext());
                //再将前连后
                cur.setNext(boy);
                //辅助变量下移
                cur = boy;
            }
        }
    }

    //遍历环形链表
    public void list() {
        //判空
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        //辅助变量
        Boy cur = first;
        while (true) {
            System.out.println(cur);
            cur = cur.getNext();
            if (cur == first) {
                break;
            }
        }
    }


    /**
     * @param startNo 编号k
     * @param countNo 数到m
     * @param nums    n个人
     */
    //根据k值,确定数据移除的顺序
    public void countBoy(int startNo, int countNo, int nums) {
        //数据校验
        if (first == null || startNo < 1 || startNo > nums) {
            throw new RuntimeException("数据有误");
        }
        //辅助变量(用来指向编号为(k-1)的数据)
        Boy help = first;
        //将help指向的数据设置为first指向的数据的前一个数据
        while (true) {
            if (help.getNext() == first) {
                break;
            }
            help = help.getNext();
        }
        //编号k,移动k-1次
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            help = help.getNext();
        }

        while (true) {
            if (first == help) {
                break;
            }
            //数到m移动m-1次,同时移动countNo-1次
            for (int i = 0; i < countNo - 1; i++) {
                first = first.getNext();
                help = help.getNext();
            }

            //
            System.out.println("出圈:" + first.getNo());
//             first.setNext(help.getNext());
            first = first.getNext();
            help.setNext(first);
        }
        //
        System.out.println("最后:" + help.getNo());
    }
}


//创建Boy类
class Boy {
    private int no;
    private Boy next;

    public Boy() {
    }

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

    @Override
    public String toString() {
        return "Boy{" +
                "no=" + no +
                '}';
    }
}

背包

package com.t_graphs;


import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @author ming
 * @create 2020-03-12 20:30
 *
 * 背包:不支持从中删除元素的集合类型;用于收集元素并迭代遍历收集到的元素。
 *
 * 基于链表的背包
 */
public class Bag<Item> implements Iterable<Item> {
    private Node<Item> first;    // 首背包
    private int n;               // 包里的元素数量

    // 辅助链表类
    private static class Node<Item> {
        private Item item;
        private Node<Item> next;
    }

    /**
     * 初始化空包。
     */
    public Bag() {
        first = null;
        n = 0;
    }

    /**
     * @return {@code true} 包为空;否则{@code false}
     */
    public boolean isEmpty() {
        return first == null;
    }

    /**
     * @return 包里的物品数量
     */
    public int size() {
        return n;
    }

    /**
     * Adds the item to this bag.
     *
     * @param  item the item to add to this bag
     */
    public void add(Item item) {
        Node<Item> oldfirst = first;
        first = new Node<Item>();
        first.item = item;
        first.next = oldfirst;
        n++;
    }


    /**
     * @return 一个迭代器,它以任意的顺序对包中的项进行迭代
     */
    public Iterator<Item> iterator()  {
        return new LinkedIterator(first);
    }

    // 迭代器不实现remove(),因为它是可选的
    private class LinkedIterator implements Iterator<Item> {
        private Node<Item> current;

        public LinkedIterator(Node<Item> first) {
            current = first;
        }

        public boolean hasNext()  { return current != null;                     }
        public void remove()      { throw new UnsupportedOperationException();  }

        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();
            Item item = current.item;
            current = current.next;
            return item;
        }
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        Bag<String> bag = new Bag<>();
        bag.add("w");
        bag.add("q");
        bag.add("r");
        bag.add("d");

        for (String s : bag) {
            System.out.println(s);	// 遍历顺序与插入顺序相反
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值