-
单链表
- 链表内存存储
头指针 | 地址 | data域 | next域 | |
head | 150 | 110 | a2 | 180 |
120 | ||||
130 | a4 | 170 |
||
140 | a6 | null | ||
150 | a1 | 110 | ||
180 | a3 | 130 |
总结:1.链表是以节点的方式存储的,是链式存储。
2.各个节点不一定连续。
3.链表分带头结点的链表和不带头节点的链表
4.每个节点包含data域,next域:指向下一个节点
- 实例
单链表基本操作
代码:
/**
* Project Name:leetcode
* File Name:SingleLinkedList.java
* Package Name:singlelinkedList
* Date:2020年2月5日下午4:18:04
* Copyright (c) 2020, [email protected] All Rights Reserved.
*
*/
package singlelinkedList;
import java.util.Stack;
/**
* ClassName:SingleLinkedList <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2020年2月5日 下午4:18:04 <br/>
*
* @author yrz
* @version
* @see
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
Node node1 = new Node(1, "宋江", "及时雨");
Node node2 = new Node(2, "吴用", "智多星");
Node node3 = new Node(3, "卢俊义", "玉麒麟");
Node node4 = new Node(4, "林冲", "豹子头");
// singleLinkedList.add(node1);
// singleLinkedList.add(node4);
// singleLinkedList.add(node3);
// singleLinkedList.add(node2);
singleLinkedList.addByOrder(node1);
singleLinkedList.addByOrder(node2);
singleLinkedList.addByOrder(node3);
singleLinkedList.addByOrder(node4);
singleLinkedList.List();
System.out.println();
// Node node5 = new Node(3, "鲁智深", "花和尚");
// singleLinkedList.update(node5);
// singleLinkedList.List();
// System.out.println();
// singleLinkedList.delete(node5);
// singleLinkedList.List();
// int res = SingleLinkedListDemo.getLength(singleLinkedList.getHeadNode());
// System.out.println("res=" + res);
//
// System.out.println();
// 找到倒数第key个节点
// Node indexNode = SingleLinkedListDemo.getNodeInfo(singleLinkedList.getHeadNode(), 1);
//
// System.out.println(indexNode);
// 反转链表
// System.out.println();
// SingleLinkedListDemo.reverseSingLinkedList(singleLinkedList.getHeadNode());
//
// singleLinkedList.List();
//倒序输出链表
System.out.println("-------------");
SingleLinkedListDemo.reverseprint(singleLinkedList.getHeadNode());
}
// 获取单链表有效个数
public static int getLength(Node headNode) {
// 链表为空,返回0
if (headNode.next == null) {
return 0;
}
Node temp = headNode;// 临时指针变量,用于遍历链表节点使用。
int count = 0;// 记录有效节点的变量
while (true) {
if (temp.next != null) {
count++;
temp = temp.next;
} else {
break;
}
}
return count;
}
// 获取倒数第K个节点信息
public static Node getNodeInfo(Node headNode, int index) {
// 空链表
if (headNode.next == null) {
return null;
}
// 获取链表大小
int size = 0;
Node temp = headNode;
size = SingleLinkedListDemo.getLength(headNode);
// index校验
if (index < 0 || index > size) {
return null;
}
// 找到倒数倒数第k个节点位置
for (int i = 0; i <= size - index; i++) {
temp = temp.next;
}
return temp;
}
// 单链表反转
public static void reverseSingLinkedList(Node headNode) {
if (headNode.next == null) {
return;
}
Node temp = headNode;
Node curNextNode = null;// 当前节点的下一个节点
Node reverseNode = new Node(0, "", "");
while (temp.next != null) {
curNextNode = temp.next.next;// 当前有效节点的下一个节点
temp.next.next = reverseNode.next;// 新链表的头节点的下一个节点给反转链表头节点的下一个节点
reverseNode.next = temp.next;// 反转链表头节点和新节点连接
temp.next = curNextNode;// 指针后移
}
headNode.next = reverseNode.next;
}
// 从尾到头打印单链表(使用栈)
public static void reverseprint(Node headNode) {
if (headNode.next == null) {
return ;
}
Node temp = headNode;
Stack<Node> stack = new Stack<Node>();
while (temp.next != null) {
stack.add(temp.next);
temp = temp.next;
}
while(stack.size()>0) {
System.out.println(stack.pop());
}
}
}
//创建链表
class SingleLinkedList {
// 初始化头节点
Node headNode = new Node(0, "", "");
public Node getHeadNode() {
return headNode;
}
// 直接添加链表尾部
public void add(Node node) {
Node temp = headNode;// 指针变量,从头节点开始遍历
while (true) {
// 判断是不是链表尾部
if (temp.next == null) {
break;
}
// temp后移
temp = temp.next;
}
// 此时指针指向最后一个节点,节点的next指向新节点。添加元素完成
temp.next = node;
}
// 添加节点时,根据no将英雄插入指定位置
public void addByOrder(Node node) {
boolean flag = false;// 用于判断添加节点是否重复
Node temp = headNode;
while (true) {
// 判断是否是链表尾部,是的话直接添加
if (temp.next == null) {
break;
}
// 判断添加节点是否重复
if (temp.next.no == node.no) {
flag = true;
break;
}
// 判断插入位置
if (temp.next.no > node.no) {
break;
}
// 指针后移
temp = temp.next;
}
if (flag) {
System.out.println("当前添加节点已存在,编号是" + node.no);
} else {
// 插入新节点
node.next = temp.next;
temp.next = node;
}
}
// 修改节点
public void update(Node node) {
Node temp = headNode;
boolean flag = false;
while (true) {
// 判断链表是否为空
// if (temp.next == null) {
// break;
// }
// 找到要修改的节点
if (temp.no == node.no) {
flag = true;
break;
}
// 指针后移
temp = temp.next;
}
if (flag) {
temp.name = node.name;
temp.nickname = node.nickname;
}
}
// 删除指定节点
public void delete(Node node) {
Node temp = headNode;
boolean flag = false;
while (true) {
// 链表最后,退出
if (temp.next == null) {
break;
}
// 找到要删除的节点的前一个节点
if (node.no == temp.next.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.printf("要删除的%d节点不存在\n", node.no);
}
}
// 遍历链表
public void List() {
Node temp = headNode;
while (true) {
// 判断链表是否为空
if (temp.next != null) {
System.out.println(temp.next.toString());
temp = temp.next;
} else {
break;
}
}
}
}
//创建节点
class Node {
int no;
String name;
String nickname;
Node next;
public Node(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "Node [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}
- 面试题
一. 获取单链表有效个数
// 获取倒数第K个节点信息
public static Node getNodeInfo(Node headNode, int index) {
// 空链表
if (headNode.next == null) {
return null;
}
// 获取链表大小
int size = 0;
Node temp = headNode;
size = SingleLinkedListDemo.getLength(headNode);
// index校验
if (index < 0 || index > size) {
return null;
}
// 找到倒数倒数第k个节点位置
for (int i = 0; i <= size - index; i++) {
temp = temp.next;
}
return temp;
}
二. 单链表反转
思路分析:
// 单链表反转
public static void reverseSingLinkedList(Node headNode) {
if (headNode.next == null) {
return;
}
Node temp = headNode;
Node curNextNode = null;// 当前节点的下一个节点
Node reverseNode = new Node(0, "", "");
while (temp.next != null) {
curNextNode = temp.next.next;// 当前有效节点的下一个节点
temp.next.next = reverseNode.next;// 新链表的头节点的下一个节点给反转链表头节点的下一个节点
reverseNode.next = temp.next;// 反转链表头节点和新节点连接
temp.next = curNextNode;// 指针后移
}
headNode.next = reverseNode.next;
}
三. 从尾到头打印单链表(使用栈)
// 从尾到头打印单链表(使用栈)
public static void reverseprint(Node headNode) {
if (headNode.next == null) {
return ;
}
Node temp = headNode;
Stack<Node> stack = new Stack<Node>();
while (temp.next != null) {
stack.add(temp.next);
temp = temp.next;
}
while(stack.size()>0) {
System.out.println(stack.pop());
}
}
2.双链表
- 单向链表的缺点
查找方向只有一个,双向链表可以向前或者向后查找
双向链表可以可以自行删除节点,单向节点不能自动删除,需要辅助节点
双向链表基本操作:
思路
代码实现:
/**
* Project Name:leetcode
* File Name:DoubleLinkedList.java
* Package Name:singlelinkedList
* Date:2020年2月7日上午11:36:36
* Copyright (c) 2020, [email protected] All Rights Reserved.
*
*/
package singlelinkedList;
/**
* ClassName:DoubleLinkedList <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2020年2月7日 上午11:36:36 <br/>
*
* @author yrz
* @version
* @see
*/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
Node2 node1 = new Node2(1, "宋江", "及时雨");
Node2 node2 = new Node2(2, "吴用", "智多星");
Node2 node3 = new Node2(3, "卢俊义", "玉麒麟");
Node2 node6 = new Node2(6, "林冲", "豹子头");
doubleLinkedList.addData(node1);
doubleLinkedList.addData(node2);
doubleLinkedList.addData(node3);
doubleLinkedList.addData(node6);
doubleLinkedList.List();
System.out.println();
Node2 node4 = new Node2(4, "鲁智深", "花和尚");
doubleLinkedList.addByOrder(node4);
doubleLinkedList.List();
System.out.println();
// 删除节点
doubleLinkedList.deleteNode(6);
doubleLinkedList.List();
// 修改节点信息
System.out.println();
doubleLinkedList.update(new Node2(4, "xxx", "ccc"));
doubleLinkedList.List();
}
}
//双向链表
class DoubleLinkedList {
// 初始化头节点
Node2 headNode = new Node2(0, "", "");
public Node2 getHeadNode() {
return headNode;
}
// 添加节点到链表尾部
public void addData(Node2 node) {
Node2 temp = headNode;
while (true) {
// 判断是否是链表尾部,是的话直接添加
if (temp.next == null) {
break;
} else {
temp = temp.next;
}
}
temp.next = node;
node.pre = temp;
}
// 添加按顺序插入节点
public void addByOrder(Node2 node) {
boolean flag = false;// 用于判断添加节点是否重复
Node2 temp = headNode;
while (true) {
// 判断是否是链表尾部,是的话直接添加
if (temp.next == null) {
break;
}
// 判断添加节点是否重复
if (temp.next.no == node.no) {
flag = true;
break;
}
// 判断插入位置
if (temp.next.no > node.no) {
break;
}
// 指针后移
temp = temp.next;
}
if (flag) {
System.out.println("当前添加节点已存在,编号是" + node.no);
} else {
// 插入新节点
// 插入节点和后一个节点连接
temp.next.pre = node;
node.next = temp.next;
// 插入节点和前一个节点连接
temp.next = node;
node.pre = temp;
}
}
// 删除节点
public void deleteNode(int no) {
Node2 temp = headNode;
boolean flag = false;
while (true) {
// 链表最后,退出
if (temp.next == null) {
break;
}
// 找到要删除的节点的前一个节点
if (no == temp.next.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
//最后一个节点,释放指针
if (temp.next.next != null) {
//后继元素的前驱指向前一个元素
temp.next.next.pre = temp.next.pre;
}
//前驱元素的后继指向后继元素
temp.next.pre.next = temp.next.next;
// temp.next = temp.next.next;
} else {
System.out.printf("要删除的%d节点不存在\n", no);
}
}
// 修改节点
public void update(Node2 node) {
Node2 temp = headNode;
boolean flag = false;
while (true) {
// 判断链表是否为空
// if (temp.next == null) {
// break;
// }
// 找到要修改的节点
if (temp.no == node.no) {
flag = true;
break;
}
// 指针后移
temp = temp.next;
}
if (flag) {
temp.name = node.name;
temp.nickname = node.nickname;
}
}
// 遍历链表
public void List() {
Node2 temp = headNode;
while (true) {
// 判断链表是否为空
if (temp.next != null) {
System.out.println(temp.next.toString());
temp = temp.next;
} else {
break;
}
}
}
}
//创建节点
class Node2 {
int no;
String name;
String nickname;
Node2 next;// 后继节点
Node2 pre;// 前驱节点
public Node2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "Node [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}
3.循环链表
- Josephu约瑟夫问题
思路:
1.创建一个环形链表来表示这个圈:
2.出队列
代码实现:
/**
* Project Name:leetcode
* File Name:Josephu.java
* Package Name:singlelinkedList
* Date:2020年2月8日下午10:07:05
* Copyright (c) 2020, [email protected] All Rights Reserved.
*
*/
package singlelinkedList;
/**
* ClassName:Josephu <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2020年2月8日 下午10:07:05 <br/>
*
* @author yrz
* @version
* @see
*/
public class Josephu {
public static void main(String[] args) {
CircularLinkedList circularLinkedList = new CircularLinkedList();
circularLinkedList.add(15);
circularLinkedList.List();
System.out.println();
circularLinkedList.countNode(1, 2, 15);
}
}
//创建循环链表
class CircularLinkedList {
//第一个节点
Node3 first = null;
// 添加节点
public void add(int nums) {
// 校验nums
if (nums < 1) {
return;
}
// 辅助指针
Node3 temp = null;
for (int i = 1; i <= nums; i++) {
// 第一个节点
Node3 node = new Node3(i);
if (i == 1) {
first = node;
first.setNext(node);// 构成环
temp = first;
} else {
temp.setNext(node);// 指向下一个节点
node.setNext(first);// 构成环
temp = node;// 指针后移
}
}
}
/**
* 出环
* @author Administrator
* @param startNo--从第几个开始
* @param count--数几下
* @param nums --一共几个节点
* @since JDK 1.6
*/
public void countNode(int startNo, int count, int nums) {
Node3 temp = first;
// 校验数据
if (startNo < 1 || count > nums) {
System.out.println("数据错误");
return;
}
// 事先指向链表最后一个节点
while (true) {
if (temp.getNext() == first) {
break;
}
temp = temp.getNext();
}
// 报数前,将first和temp移动startNo-1次
for (int i = 0; i < startNo - 1; i++) {
first = first.getNext();
temp = temp.getNext();
}
// 报数时,将first和temp移动count-1次
while (true) {
if (temp == first) {
break;
}
for (int i = 0; i < count - 1; i++) {
first = first.getNext();
temp = temp.getNext();
}
// 出圈
System.out.printf("出圈小孩编号%d\n", first.getNo());
first = first.next;
temp.next = first;
}
System.out.printf("最后一个出圈小孩编号%d\n", first.getNo());
}
// 遍历链表
public void List() {
Node3 temp = first;
while (true) {
System.out.printf("编号%d\n", temp.getNo());
if (temp.next == first) {
break;
}
temp = temp.next;
}
}
}
//创建节点
class Node3 {
int no;
Node3 next;
public Node3(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node3 getNext() {
return next;
}
public void setNext(Node3 next) {
this.next = next;
}
}