一:单链表
单链表的结点结构 数据域+指针域
结点
- 对每个数据元素 Ai,除了存储其本身的信息之外
- 还需存储一个指示其直接后继存放位置的指针。
public class Node<T> {
public T data; // 数据域
public Node<T> next; // 指针域
// 一个参数的构造方法
public Node(Node<T> next) {
super();
this.next = next;
}
// 两个参数的构造方法
public Node(T data, Node<T> next) {
super();
this.data = data;
this.next = next;
}
/**
* @return the data
*/
public T getData() {
return data;
}
/**
* @return the next
*/
public Node<T> getNext() {
return next;
}
}
单链表
- 线性表的链式存储结构是用一组任意的存储单元来存放线性表的数据元素
- 这组存储单元可以是连续的,也可以是不连续的
注意点:
- 单链表的插入与删除 ,只需修改插入位置之前的结点 和当前插入结点的指针指向
- 插入与删除,即使插入/删除的数据在最后,也要遍历一遍
//单链表存储结构 头指针+单链表长度
public class LinkList<T> {
private Node<T> head; // 头指针 指向 头结点
private int length; // 链表长度
// 空构造器 链表初始化
public LinkList() {//创建一个空的头结点(存在)
head = new Node<T>(null); //this.next = null
length = 0;
}
//获取头指针
public Node<T> getHead(){
return this.head;
}
// 在指定位置添加 结点
public boolean add(T obj , int pos) {
//判断pos值是否合法
if(pos < 1 || pos > length +1) {
System.out.println("添加操作的pos值不合法");
return false;
}
Node<T> p = head;
Node<T> q = p.next;
int count = 1;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
p.next = new Node<T>(obj,q);
length ++;
return true;
}
// 移除 指定位置的结点
public boolean remove(int pos) {
//判断链表是否为空
if(isEmpty()) {
System.out.println("链表为空,无法 移除元素");
return false;
}
//判断pos值是否合法
if(pos < 1 || pos > length) {
System.out.println("移除的pos值不合法");
return false;
}
Node<T> p = head;
Node<T> q = p.next;
int count = 1;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
p.next = q.next;
length --;
return true;
}
// 获取 指定位置结点的数据
public T value(int pos) {
//判断链表是否为空
if(isEmpty()) {
System.out.println("链表为空,无法获取指定位置结点的数据");
return null;
}
//判断pos值是否合法
if(pos < 1 || pos > length) {
System.out.println("获取指定位置结点的pos值不合法");
return null;
}
Node<T> p = head;
int count = 1;
while(count < pos) {
p = p.next;
count ++;
}
return p.next.getData();
}
//单链表的查找
public int find(T obj) {
//判断链表是否为空
if(isEmpty()) {
System.out.println("链表为空,无法查找数据");
return -1;
}
int count = 1;
Node<T> p = head.next;
while(p != null) {
if(p.getData().equals(obj)) {
return count;
}
p = p.next;
count++;
}
return -1;
}
//更新单链表第pos个结点的值
public boolean modify(T obj,int pos) {
//判断链表是否为空
if(isEmpty()) {
System.out.println("链表为空,无法更新单链表结点的值");
return false;
}
//判断pos值是否合法
if(pos < 1 || pos > length) {
System.out.println("更新单链表结点的pos值不合法");
return false;
}
Node<T> p = head.next;
int count = 1;
while(count < pos) {
p = p.next;
count ++;
}
p.data = obj;
return true;
}
//判断链表是否为空
public boolean isEmpty() {
return length == 0 ? true : false;
}
//求数据元素个数
public int getSize() {
return this.length;
}
// 清空链表
public void clear() {
length = 0;
this.head.next = null;
}
// 循环遍历链表元素
public void nextOrder() {
//判断链表是否为空
if(isEmpty()) {
System.out.println("链表为空,无法获循环遍历链表元素");
return;
}
Node<T> p = head.next;
System.out.println("链表元素正在打印...");
while(p != null) {
System.out.print(p.getData()+"\t");
p = p.next;
}
System.out.println("\n链表元素打印完毕...");
}
}
单链表测试Demo
package 链表.单链表;
public class LinkListTest {
public static void main(String[] args) {
LinkList<Integer> link = new LinkList<>();
// 添加
link.add(11, 1);
link.add(21, 2);
link.add(31, 3);
link.add(41, 4);
link.add(51, 5);
link.add(61, 6);
// 移除
link.remove(3);
// 获取 指定位置结点的数据
System.out.println(link.value(4));
// 搜素元素
System.out.println("元素下标为:"+link.find(31));
// 更新单链表第pos个结点的值
link.modify(66, 3);
//遍历元素
link.nextOrder();
}
}
二:双向链表
双向链表 的结点结构 前指针域 + 数据域 + 后指针域
结点
public class Node<T> {
T data; // 数据域
Node<T> next; // 后指针
Node<T> previous; // 前指针
//带 1个参数的构造器 初始化 head
public Node(Node<T> next) {
super();
this.next = next;
this.previous = null;
}
//带三个参数的构造器
public Node(T data, Node<T> next, Node<T> previous) {
super();
this.data = data;
this.next = next;
this.previous = previous;
}
/**
* @return the data
*/
public T getData() {
return data;
}
/**
* @return the next
*/
public Node<T> getNext() {
return next;
}
/**
* @return the previous
*/
public Node<T> getPrevious() {
return previous;
}
}
双向链表
public class DoubleLinkList<T> {
private Node<T> head; //头指针
private int length; //链表长度
//空构造器 初始化双向链表
public DoubleLinkList() {
super();
this.head = new Node<T>(null);
this.length = 0;
}
//在指定位置添加元素
public boolean add(T data , int pos) {
//判断pos值是否合法
if(pos < 1 || pos > length +1) {
System.out.println("当前要插入的pos值不合法");
return false;
}
int count = 1;
Node<T> p = head;
Node<T> q = p.next;
while(count < pos) {
p = p.next;
q = q.next;
count++;
}
if(q != null) {
p.next = q.previous = new Node<T>(data,q,p);
}else {
p.next = new Node<T>(data,q,p);
}
length++;
return true;
}
//删除指定位置的元素
public boolean remove(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向链表为空,无法遍历");
return false;
}
//判断pos值合法性
if(pos < 1 || pos > length) {
System.out.println("当前要移除的pos值不合法");
return false;
}
int count = 1;
Node<T> p = head;
Node<T> q = head.next;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
//此时 q就是要删除的元素
if(q.next != null) { // 如果q不是尾部
p.next = q.next;
q.next.previous = p;
}else { // q是尾部
p.next = q.next;
}
length--;
return true;
}
//修改指定位置的元素
public boolean modify(T data, int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向链表为空,无法修改元素");
return false;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前要修改的pos值不合法");
return false;
}
int count = 1;
Node<T> q = head.next;
while(count < pos) {
q = q.next;
count ++;
}
//当前q就是要修改的那个结点
q.data = data;
return true;
}
//查找指定元素的位置
public int findPos(T data) {
//判空
if(isEmpty()) {
System.out.println("当前双向链表为空,无法查找元素");
return -1;
}
int count = 1;
Node<T> p = head.next;
while( p != null) {
if(p.getData().equals(data)) {
return count;
}
count ++;
p = p.next;
}
return -1;
}
//查找指定位置的元素
public T findData(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向链表为空,无法查找指定位置的元素");
return null;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前指定的pos值不合法");
return null;
}
int count =1;
Node<T> p = head.next;
while( count < pos ) {
p = p.next;
count++;
}
//已经找到目标位置元素 p
return p.getData();
}
//判空
public boolean isEmpty() {
return length == 0? true:false;
}
//获取双向链表长度
public int getSize() {
return this.length;
}
//循环遍历链表
public void nextOrder() {
//判空
if(isEmpty()) {
System.out.println("当前双向链表为空,无法遍历");
return ;
}
System.out.println("\n双向链表开始遍历...");
Node<T> p = head.next;
while(p!=null) {
System.out.print(p.getData()+"\t");
p = p.next;
}
System.out.println("\n双向链表遍历结束...");
}
//清空双向链表
public void clear() {
length = 0;
head.next = null;
}
}
双向链表测试Demo
public class DoubleLinkLIstTest {
public static void main(String[] args) {
DoubleLinkList<Integer> link = new DoubleLinkList<>();
//添加
link.add(111, 1);
link.add(222, 2);
link.add(333, 3);
// 删除
link.remove(3);
// 修改
link.modify(444, 3);
// 查找指定元素的位置
System.out.println(link.findPos(222));
// 查找指定位置的元素
System.out.println(link.findData(3));
// 获取双向链表长度
System.out.println("双向链表的长度为:"+link.getSize());
// 清空
link.clear();
// 遍历输出
link.nextOrder();
}
}
三:单向循环链表
循环链表是另一种形式的链表,它的特点是表中最后一个结点的指针域不再为空,而是指向表头结点,整个链表形成一个环。
和单向链表的区别是 最后一个元素不再指向null 而是指向 head
单向循环结点
public class SingleLoopNode<T> {
T data; //数据域
SingleLoopNode<T> next; //指针域
//单参数构造器
public SingleLoopNode(SingleLoopNode<T> next) {
super();
this.next = next;
}
// 双参数构造器
public SingleLoopNode(T data, SingleLoopNode<T> next) {
super();
this.data = data;
this.next = next;
}
/**
* @return the data
*/
public T getData() {
return data;
}
}
单向循环链表
public class SingleLoopLinkList<T> {
private SingleLoopNode<T> head; //头指针
private int length; //单向循环链表长度
//初始化链表 head不再指向null 而是他自己
public SingleLoopLinkList() {
super();
this.head = new SingleLoopNode<T>(head);
length = 0;
}
//在指定位置添加元素
public boolean add(T data , int pos) {
//判断pos值是否合法
if(pos < 1 || pos > length +1) {
System.out.println("当前要插入的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> p = head;
SingleLoopNode<T> q = p.next;
while(count < pos) {
p = p.next;
q = q.next;
count++;
}
if(q == null) {
p.next = new SingleLoopNode<T>(data,head);
}else {
p.next = new SingleLoopNode<T>(data,q);
}
length++;
return true;
}
//删除指定位置的元素
public boolean remove(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法遍历");
return false;
}
//判断pos值合法性
if(pos < 1 || pos > length) {
System.out.println("当前要移除的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> p = head;
SingleLoopNode<T> q = head.next;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
//此时 q就是要删除的元素
p.next = q.next;
length--;
return true;
}
//修改指定位置的元素
public boolean modify(T data, int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法修改元素");
return false;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前要修改的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> q = head.next;
while(count < pos) {
q = q.next;
count ++;
}
//当前q就是要修改的那个结点
q.data = data;
return true;
}
//查找指定元素的位置
public int findPos(T data) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法查找元素");
return -1;
}
int count = 1;
SingleLoopNode<T> p = head.next;
while( p != head) {
if(p.getData().equals(data)) {
return count;
}
count ++;
p = p.next;
}
return -1;
}
//查找指定位置的元素
public T findData(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法查找指定位置的元素");
return null;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前指定的pos值不合法");
return null;
}
int count =1;
SingleLoopNode<T> p = head.next;
while( count < pos ) {
p = p.next;
count++;
}
//已经找到目标位置元素 p
return p.getData();
}
//判空
public boolean isEmpty() {
return length == 0? true:false;
}
//获取单向循环链表长度
public int getSize() {
return this.length;
}
//循环遍历链表
public void nextOrder() {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法遍历");
return ;
}
System.out.println("\n单向循环链表开始遍历...");
SingleLoopNode<T> p = head.next;
while(p!=head) {
System.out.print(p.getData()+"\t");
p = p.next;
}
System.out.println("\n单向循环链表遍历结束...");
}
//清空单向循环链表
public void clear() {
length = 0;
head.next = null;
}
}
单向循环链表 测试Demo
public class SingleLoopLinkList<T> {
private SingleLoopNode<T> head; //头指针
private int length; //单向循环链表长度
//初始化链表 head不再指向null 而是他自己
public SingleLoopLinkList() {
super();
this.head = new SingleLoopNode<T>(head);
length = 0;
}
//在指定位置添加元素
public boolean add(T data , int pos) {
//判断pos值是否合法
if(pos < 1 || pos > length +1) {
System.out.println("当前要插入的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> p = head;
SingleLoopNode<T> q = p.next;
while(count < pos) {
p = p.next;
q = q.next;
count++;
}
if(q == null) {
p.next = new SingleLoopNode<T>(data,head);
}else {
p.next = new SingleLoopNode<T>(data,q);
}
length++;
return true;
}
//删除指定位置的元素
public boolean remove(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法遍历");
return false;
}
//判断pos值合法性
if(pos < 1 || pos > length) {
System.out.println("当前要移除的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> p = head;
SingleLoopNode<T> q = head.next;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
//此时 q就是要删除的元素
p.next = q.next;
length--;
return true;
}
//修改指定位置的元素
public boolean modify(T data, int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法修改元素");
return false;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前要修改的pos值不合法");
return false;
}
int count = 1;
SingleLoopNode<T> q = head.next;
while(count < pos) {
q = q.next;
count ++;
}
//当前q就是要修改的那个结点
q.data = data;
return true;
}
//查找指定元素的位置
public int findPos(T data) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法查找元素");
return -1;
}
int count = 1;
SingleLoopNode<T> p = head.next;
while( p != head) {
if(p.getData().equals(data)) {
return count;
}
count ++;
p = p.next;
}
return -1;
}
//查找指定位置的元素
public T findData(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法查找指定位置的元素");
return null;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前指定的pos值不合法");
return null;
}
int count =1;
SingleLoopNode<T> p = head.next;
while( count < pos ) {
p = p.next;
count++;
}
//已经找到目标位置元素 p
return p.getData();
}
//判空
public boolean isEmpty() {
return length == 0? true:false;
}
//获取单向循环链表长度
public int getSize() {
return this.length;
}
//循环遍历链表
public void nextOrder() {
//判空
if(isEmpty()) {
System.out.println("当前单向循环链表为空,无法遍历");
return ;
}
System.out.println("\n单向循环链表开始遍历...");
SingleLoopNode<T> p = head.next;
while(p!=head) {
System.out.print(p.getData()+"\t");
p = p.next;
}
System.out.println("\n单向循环链表遍历结束...");
}
//清空单向循环链表
public void clear() {
length = 0;
head.next = null;
}
}
四:双向循环链表
在双向链表中,若变量p引用某个结点,则显然有:p.next.prior == p.prior.next = =p
双向循环结点
public class DoubleLoopNode<T> {
T data; // 数据域
DoubleLoopNode<T> next; // 后指针
DoubleLoopNode<T> previous; // 前指针
//初始化head 重要步骤 先将head的前后指针通通指向自己
public DoubleLoopNode() {
super();
this.data = null;
this.next = this;
this.previous = this;
}
//带三个参数的构造器
public DoubleLoopNode(T data, DoubleLoopNode<T> next, DoubleLoopNode<T> previous) {
super();
this.data = data;
this.next = next;
this.previous = previous;
}
/**
* @return the data
*/
public T getData() {
return data;
}
/**
* @return the next
*/
public DoubleLoopNode<T> getNext() {
return next;
}
/**
* @return the previous
*/
public DoubleLoopNode<T> getPrevious() {
return previous;
}
}
双向循环链表
public class DoubleLoopLinkList<T> {
private DoubleLoopNode<T> head; //头指针
private int length; //链表长度
//空构造器 初始化双向循环链表 head 的前后指针指向自己
public DoubleLoopLinkList() {
super();
this.head = new DoubleLoopNode<T>();
this.length = 0;
}
//在指定位置添加元素
public boolean add(T data , int pos) {
//判断pos值是否合法
if(pos < 1 || pos > length +1) {
System.out.println("当前要插入的pos值不合法");
return false;
}
int count = 1;
DoubleLoopNode<T> p = head;
DoubleLoopNode<T> q = p.next;
while(count < pos) {
p = p.next;
q = q.next;
count++;
}
if(q != head) {
p.next = q.previous = new DoubleLoopNode<T>(data,q,p);
}else {
p.next = head.previous = new DoubleLoopNode<T>(data,head,p);
}
length++;
return true;
}
//删除指定位置的元素
public boolean remove(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向循环链表为空,无法遍历");
return false;
}
//判断pos值合法性
if(pos < 1 || pos > length) {
System.out.println("当前要移除的pos值不合法");
return false;
}
int count = 1;
DoubleLoopNode<T> p = head;
DoubleLoopNode<T> q = head.next;
while(count < pos) {
p = p.next;
q = q.next;
count ++;
}
//此时 q就是要删除的元素
if(q.next != head) { // 如果q不是尾部
p.next = q.next;
q.next.previous = p;
}else { // q是尾部
p.next = head;
head.previous = p;
}
length--;
return true;
}
//修改指定位置的元素
public boolean modify(T data, int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向循环链表为空,无法修改元素");
return false;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前要修改的pos值不合法");
return false;
}
int count = 1;
DoubleLoopNode<T> q = head.next;
while(count < pos) {
q = q.next;
count ++;
}
//当前q就是要修改的那个结点
q.data = data;
return true;
}
//查找指定元素的位置
public int findPos(T data) {
//判空
if(isEmpty()) {
System.out.println("当前双向循环链表为空,无法查找元素");
return -1;
}
int count = 1;
DoubleLoopNode<T> p = head.next;
while( p != head) {
if(p.getData().equals(data)) {
return count;
}
count ++;
p = p.next;
}
return -1;
}
//查找指定位置的元素
public T findData(int pos) {
//判空
if(isEmpty()) {
System.out.println("当前双向循环链表为空,无法查找指定位置的元素");
return null;
}
//判断pos值
if(pos < 1 || pos > length) {
System.out.println("当前指定的pos值不合法");
return null;
}
int count =1;
DoubleLoopNode<T> p = head.next;
while( count < pos ) {
p = p.next;
count++;
}
//已经找到目标位置元素 p
return p.getData();
}
//判空
public boolean isEmpty() {
return length == 0? true:false;
}
//获取双向循环链表长度
public int getSize() {
return this.length;
}
//循环遍历链表
public void nextOrder() {
//判空
if(isEmpty()) {
System.out.println("当前双向循环链表为空,无法遍历");
return ;
}
System.out.println("\n双向循环链表开始遍历...");
DoubleLoopNode<T> p = head.next;
while(p!=head) {
System.out.print(p.getData()+"\t");
p = p.next;
}
System.out.println("\n双向循环链表遍历结束...");
}
//清空双向循环链表
public void clear() {
length = 0;
head.next = head;
head.previous = head;
}
}
双向循环链表测试Demo
public class DoubleLinkListTest {
public static void main(String[] args) {
DoubleLoopLinkList<Integer> link = new DoubleLoopLinkList<>();
//添加
link.add(111, 1);
link.add(222, 2);
link.add(333, 3);
// 删除
link.remove(3);
// 修改
link.modify(444, 3);
// 查找指定元素的位置
System.out.println(link.findPos(22));
// 查找指定位置的元素
System.out.println(link.findData(4));
// 获取双向链表长度
System.out.println("双向链表的长度为:"+link.getSize());
// 清空
link.clear();
// 遍历输出
link.nextOrder();
}
}
题目:
反转单向链表
//反转单向链表
public void reverseLinkList(LinkList<T> list){
//判断链表是否为空
if(list.getSize() == 0) {
return;
}
Node<T> head = list.getHead();
Node<T> firstNode = head.next;//第一个结点保持不变
while(firstNode.next != null) {
list.add(firstNode.next.getData(), 1);
firstNode.next = firstNode.next.next;
}
// 遍历输出
nextOrder();
}
反转双向链表
//反转双向链表
public void reverseLinkList(DoubleLinkList<T> list) {
//判空
if(list.getSize() == 0) {
return;
}
Node<T> head = list.head;
Node<T> end = head;
while( end.next != null) {
end = end.next;
}
while(end != null && end != head) {
System.out.print(end.getData()+"\t");
end = end.previous;
}
}
public class printCommonLinkList {
public static void printCommon(LinkList<Integer> list1 , LinkList<Integer> list2) {
Node<Integer> p = list1.getHead().next;
Node<Integer> q = list2.getHead().next;
while( p!=null && q!=null) {
if(p.getData().compareTo(q.getData())<0) {
p = p.next;
}else if(p.getData().compareTo(q.getData())>0){
q = q.next;
}else if(p.getData().compareTo(q.getData())==0){
System.out.print(q.getData()+"\t");
p = p.next;
q = q.next;
}
}
}
public static void main(String[] args) {
LinkList<Integer> list1 = new LinkList<Integer>();
LinkList<Integer> list2 = new LinkList<Integer>();
list1.add(1, 1);
list1.add(2, 2);
list1.add(3, 3);
list1.add(4, 4);
list1.add(5, 5);
list2.add(2, 1);
list2.add(4, 2);
list2.add(5, 3);
printCommon(list1,list2);
}
}
//利用栈 空间O(N)
public static boolean checkByStack(LinkList<Integer> link) {
//判断链表是否为空 或 长度为1
if(link.getSize() < 2) {
return true;
}
Stack<Node<Integer>> stack = new Stack<>();
Node<Integer> p = link.getHead().next;
Node<Integer> q = link.getHead().next;
while(p!=null) {
stack.add(p);
p = p.next;
}
while(!stack.isEmpty()) {
if(!stack.pop().getData().equals(q.getData())) {
return false;
}
q = q.next;
}
return true;
}
//利用栈 空间O(N/2)
public static boolean checkByStackPlus(LinkList<Integer> link) {
//判断链表是否为空 或 长度为1
if(link.getSize() < 2) {
return true;
}
//快慢指针优化 只存一半的内容进栈
Node<Integer> fast = link.getHead().next; // 一次走两步
Node<Integer> slow = link.getHead().next; // 一次走一步
Node<Integer> q = link.getHead().next;
while(fast.next!=null) {
fast = fast.next;
if(fast.next != null) {
fast = fast.next;
slow = slow.next;
}
}
Stack<Node<Integer>> stack = new Stack<>();
while(slow.next != null) {
stack.add(slow.next);
slow = slow.next;
}
while(!stack.isEmpty()) {
if(!stack.pop().getData().equals(q.getData())) {
return false;
}
q = q.next;
}
return true;
}