链表是有序的列表,但是它在内存中是存储如下:
链表是以节点的方式来存储,是链式存储每个节点包含 data 域(数据), next 域:指向下一个节点.
如图:发现链表的各个节点不一定是连续存储.
链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
添加(创建):
1.先创建一个head 头节点, 作用就是表示单链表的头
2.后面我们每添加一个节点,就直接加入到 链表的最后
遍历:
通过一个辅助变量(temp)遍历,帮助遍历整个链表
插入:
1.按照编号顺序添加
2.通过一个辅助变量(temp),找到插入数据的前一个节点。
3.temp.getNext(新节点)
4.将新节点.setNext(temp.getNext())
删除
1.辅助变量(temp),找到要删除的节点的前一个结点
2.将前一个结点的next指向要删除节点的next
反转
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的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
构建一个单向的环形链表思路:
先创建第一个节点, 让 first 指向第一节点,并形成环形
后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.
遍历环形链表
先让一个辅助指针(变量) cur,指向first节点
然后通过一个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); // 遍历顺序与插入顺序相反
}
}
}