定义
- 线性表:具有相同特性数据元素的一个有限序列;该序列中所包含的元素个数n为线性表的长度(n >= 0 )。ps:当n=0时线性表为空表
- 线性表的存储结构有:
- 顺序存储结构(顺序表):用一段地址连续的存储单元依次存储线性表的数据元素。
- 链式存储结构(链表):用地址连续或不连续的存储单元存储线性表数据元素。
分类
顺序表
- 顺序表即是将线性表中的所有元素按照其逻辑顺序,依次存储到从指定的存储位置开始的一块连续的存储空间中。
- 特性:
- 随机访问
- 占用连续的存储空间
- 存储分配只能预先进行(静态分配,即是初始化时指定大小)
- 插入操作需要移动多个元素
数组
- 什么是数组呢?
数组是一种线性表数据结构;它用一组连续的内存空间,来存储一组具有相同类型的数据。(顺序表)
要点:数组是线性表(即数据排列像一条线一样的结构,每个线性表上的数据最多只有前和后两个方向),数组使用连续的内存空间存储相同类型的数据。
- 数组有什么特点?
- 支持随机访问,根据下标随机访问的时间复杂度为O(1)
- 可以存储基本数据类型(Java中的ArrayList无法存储基本数据类型如:int,其存储int型数据时需要将数据封装为Integer)
- 表示多维数组时会更加直观
- 数组下标为什么是从0开始?而不是从1开始?
- 数组的下标其实就是偏移量(offset),用array表示数组首地址时,array[0]就是偏移量为0的位置,也是首地址;array[m]就是偏移量为m的位置,array[m]的地址是:base_address + m * type_size。
- 如果数组的下标从1开始,那么array[n]元素的地址为:base_address+(n-1) * type_size,这时每次随机访问数组时都会多一次减法运算,CPU就会多执行一次减法指令。
链表
-
定义:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包含两个部分:
- 数据域:存储数据元素data ;
- 指针域:存储后继指针next(存储下一个结点地址);
-
特性:
- 不支持随机访问
- 存储空间可动态分配
- 结点的存储空间利用率比顺序表低(需要部分空间用于存储后继指针next)
- 插入操作无需移动元素
链表通过指针将一组零散的内存块串联起来,其中的内存块即为“结点”
单链表
- Node结点类:
package com.linkedlist.dto;
/**
* @Author: xpengfei
* @Date: 2020/6/6 23:35
* @Description:节点实体类
*/
public class Node {
/**
* 节点数据域,用于存储数据
*/
public int data;
/**
* 节点的指针域,用于存储下一个节点的位置
*/
public Node next;
/**
* 初始化node时给数据域赋值
* @param data
*/
public Node(int data) {
this.data = data;
}
/**
* 初始化node时给数据域及指针域赋值
* @param data
* @param next
*/
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
- 添加节点
尾部添加
// 要添加的节点
Node node = new Node(addData);
if (head == null) {
head = node;
} else {
Node tempNode = head;
while (tempNode.next != null) {
tempNode = tempNode.next;
}
// 将要添加的节点,添加到链表尾部
tempNode.next = node;
}
头部添加:
// 要添加的节点
Node node = new Node(addData);
if (head == null) {
head = node;
} else {
Node temp = head;
node.next = temp;
head = node;
}
- 删除节点
/**
* 获取链表长度
*
* @return
*/
public int getLinkedListLen() {
if (head == null) {
return 0;
}
int len = 0;
Node temp = head;
while (temp != null) {
len++;
temp = temp.next;
}
return len;
}
/**
* 删除链表中第index个节点
*
* @param index 要删除的节点序号
*/
public void deleteNodeByIndex(int index) {
if (head == null || index <= 0 || index > this.getLinkedListLen()) {
return;
} else {
if (index == 1) {
head = head.next;
return;
}
Node temp = head;
while (temp != null) {
index--;
if (index == 1) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
}
}
- 插入
/**
* 将数据data插入到链表中的第index个位置
*
* @param data 待插入数据
* @param index 待插入的位置
*/
public void insertDataToLinkedListByIndex(int data, int index) {
if (index < 0) {
return;
}
if (head == null) {
head = new Node(data);
return;
}
// index 大于链表长度,插到链表尾部
if (index > this.getLinkedListLen()) {
Node node = new Node(data);
Node tempNode = head;
while (tempNode.next != null) {
tempNode = tempNode.next;
}
//插入到链表尾部
tempNode.next = node;
node.next = null;
return;
}
//插入到链表中间
Node node = new Node(data);
Node tempNode = head;
while (tempNode != null) {
index--;
// 找到待插入位置的前一个node结点,index=0表示要插到链表头结点位置
if (index == 0 || index == 1) {
break;
}
tempNode = tempNode.next;
}
if (index == 0) {
node.next = tempNode;
head = node;
} else {
node.next = tempNode.next;
tempNode.next = node;
}
}
- 查找
/**
* 查找数据在链表中第一次出现的位置
*
* @param nodeHead 链表的头结点
* @param data 待查找数据
* @return 返回数据在链表中的位置,-1表示链表中不存在待查找数据
*/
public int findNodeIndex(Node nodeHead, int data) {
int index = 0;
while (nodeHead != null) {
++index;
if (nodeHead.data == data) {
return index;
}
nodeHead = nodeHead.next;
}
return -1;
}
- 反转
/**
* 反转链表
* @return 反转后的链表的头结点
*/
public Node reverseLinkedList() {
Node currentNode = head;
Node preNode = null;
while (currentNode != null) {
Node next = currentNode.next;
currentNode.next = preNode;
preNode = currentNode;
currentNode = next;
}
return preNode;
}
单链表操作相关代码:
package com.linkedlist;
import com.linkedlist.dto.Node;
/**
* @Author: xpengfei
* @Date: 2020/6/6 22:20
* @Description:单链表
*/
public class SingleLinkedList {
/**
* 头结点
*/
protected Node head = null;
/**
* 添加节点,链尾插入节点
*
* @param addData 要添加的节点数据
*/
public Node tailAddNode(int addData) {
// 要添加的节点
Node node = new Node(addData);
if (head == null) {
head = node;
} else {
Node tempNode = head;
while (tempNode.next != null) {
tempNode = tempNode.next;
}
// 将要添加的节点,添加到链表尾部
tempNode.next = node;
}
return node;
}
/**
* 添加节点,头部插入节点
*
* @param addData 要添加的节点数据
*/
public Node headAddNode(int addData) {
Node node = new Node(addData);
if (head == null) {
head = node;
} else {
Node temp = head;
node.next = temp;
head = node;
}
return node;
}
/**
* 获取链表长度
*
* @return
*/
public int getLinkedListLen() {
if (head == null) {
return 0;
}
int len = 0;
Node temp = head;
while (temp != null) {
len++;
temp = temp.next;
}
return len;
}
/**
* 删除链表中第index个节点
*
* @param index 要删除的节点序号
*/
public void deleteNodeByIndex(int index) {
if (head == null || index <= 0 || index > this.getLinkedListLen()) {
return;
} else {
if (index == 1) {
head = head.next;
return;
}
Node temp = head;
while (temp != null) {
index--;
if (index == 1) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
}
}
/**
* 降序排序
* 冒泡排序
*/
public void sortByDesc() {
// 小于等于1个节点时无需排序
if (this.getLinkedListLen() <= 1) {
return;
}
Node currentNode;
Node nextNode;
for (currentNode = head; currentNode.next != null; currentNode = currentNode.next) {
for (nextNode = head; nextNode.next != null; nextNode = nextNode.next) {
if (nextNode.next.data > nextNode.data) {
int tempData = nextNode.next.data;
nextNode.next.data = nextNode.data;
nextNode.data = tempData;
}
}
}
}
/**
* 升序排序
* 冒泡排序
*/
public void sortByAsc() {
// 小于等于1个节点时无需排序
if (this.getLinkedListLen() <= 1) {
return;
}
Node currentNode;
Node nextNode;
for (currentNode = head; currentNode.next != null; currentNode = currentNode.next) {
for (nextNode = head; nextNode.next != null; nextNode = nextNode.next) {
if (nextNode.data > nextNode.next.data) {
int tempData = nextNode.data;
nextNode.data = nextNode.next.data;
nextNode.next.data = tempData;
}
}
}
}
/**
* 查找数据在链表中第一次出现的位置
*
* @param nodeHead 链表的头结点
* @param data 待查找数据
* @return 返回数据在链表中的位置,-1表示链表中不存在待查找数据
*/
public int findNodeIndex(Node nodeHead, int data) {
int index = 0;
while (nodeHead != null) {
++index;
if (nodeHead.data == data) {
return index;
}
nodeHead = nodeHead.next;
}
return -1;
}
/**
* 将数据data插入到链表中的第index个位置
*
* @param data 待插入数据
* @param index 待插入的位置
*/
public void insertDataToLinkedListByIndex(int data, int index) {
if (index < 0) {
return;
}
if (head == null) {
head = new Node(data);
return;
}
// index 大于链表长度,插到链表尾部
if (index > this.getLinkedListLen()) {
Node node = new Node(data);
Node tempNode = head;
while (tempNode.next != null) {
tempNode = tempNode.next;
}
//插入到链表尾部
tempNode.next = node;
node.next = null;
return;
}
//插入到链表中间
Node node = new Node(data);
Node tempNode = head;
while (tempNode != null) {
index--;
// 找到待插入位置的前一个node结点,index=0表示要插到链表头结点位置
if (index == 0 || index == 1) {
break;
}
tempNode = tempNode.next;
}
if (index == 0) {
node.next = tempNode;
head = node;
} else {
node.next = tempNode.next;
tempNode.next = node;
}
}
/**
* 反转链表
*
* @return 反转后的链表的头结点
*/
public Node reverseLinkedList() {
Node currentNode = head;
Node preNode = null;
while (currentNode != null) {
Node next = currentNode.next;
currentNode.next = preNode;
preNode = currentNode;
currentNode = next;
}
return preNode;
}
/**
* 打印出链表中的所有节点
*/
public void printLinkList() {
System.out.println("--------------------链表输出开始----------------------");
Node temp = head;
while (temp != null) {
System.out.print(temp.data + "\t");
temp = temp.next;
}
System.out.println();
System.out.println("--------------------链表输出结束----------------------");
}
}
单链表操作单元测试:
package com.linkedlist;
import com.linkedlist.dto.Node;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* @Author: xpengfei
* @Date: 2020/6/7 14:35
* @Description:
*/
public class SingleLinkedListTest {
/**
* 链表添加节点
* 测试尾插法
*/
@Test(priority = 1)
public void tailAddNode() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.tailAddNode(1);
singleLinkedList.tailAddNode(2);
singleLinkedList.tailAddNode(3);
singleLinkedList.tailAddNode(4);
singleLinkedList.tailAddNode(5);
singleLinkedList.tailAddNode(6);
singleLinkedList.printLinkList();
Assert.assertTrue(true);
}
/**
* 链表添加节点
* 测试头插法
*/
@Test(priority = 2)
public void headAddNode() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.printLinkList();
Assert.assertTrue(true);
}
/**
* 测试获取链表长度
*/
@Test(priority = 3)
public void getLinkedListLen() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
}
/**
* 测试删除节点
* 删除的index小于链表长度
*/
@Test(priority = 4)
public void deleteNodeByIndex() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.deleteNodeByIndex(0);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
}
/**
* 测试删除节点
* 删除第1个
*/
@Test(priority = 5)
public void deleteNodeByIndex0() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.deleteNodeByIndex(1);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
}
/**
* 测试删除节点
* 删除第3个
*/
@Test(priority = 6)
public void deleteNodeByIndex1() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.deleteNodeByIndex(3);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
}
/**
* 测试删除节点
* 删除最后一个
*/
@Test(priority = 7)
public void deleteNodeByIndex2() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.deleteNodeByIndex(6);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
}
/**
* 测试删除节点
* 删除的index大于链表长度
*/
@Test(priority = 8)
public void deleteNodeByIndex3() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.headAddNode(1);
singleLinkedList.headAddNode(2);
singleLinkedList.headAddNode(3);
singleLinkedList.headAddNode(4);
singleLinkedList.headAddNode(5);
singleLinkedList.headAddNode(6);
singleLinkedList.deleteNodeByIndex(1000);
Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
}
/**
* 测试降序排序
* 冒泡
*/
@Test(priority = 9)
public void sortByDesc() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.tailAddNode(1);
singleLinkedList.tailAddNode(8);
singleLinkedList.tailAddNode(3);
singleLinkedList.tailAddNode(6);
singleLinkedList.tailAddNode(9);
singleLinkedList.tailAddNode(2);
System.out.println("*****************排序之前******************");
singleLinkedList.printLinkList();
System.out.println("*****************排序之后******************");
singleLinkedList.sortByDesc();
singleLinkedList.printLinkList();
}
/**
* 升序排序
* 冒泡
*/
@Test(priority = 10)
public void sortByAsc() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.tailAddNode(1);
singleLinkedList.tailAddNode(8);
singleLinkedList.tailAddNode(3);
singleLinkedList.tailAddNode(6);
singleLinkedList.tailAddNode(9);
singleLinkedList.tailAddNode(2);
System.out.println("*****************排序之前******************");
singleLinkedList.printLinkList();
System.out.println("*****************排序之后******************");
singleLinkedList.sortByAsc();
singleLinkedList.printLinkList();
}
/**
* 链表反转
*/
@Test(priority = 11)
public void reverseLinkedList() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.tailAddNode(1);
singleLinkedList.tailAddNode(8);
singleLinkedList.tailAddNode(3);
singleLinkedList.tailAddNode(6);
singleLinkedList.tailAddNode(9);
singleLinkedList.tailAddNode(2);
System.out.println("**************链表反转之前*************");
singleLinkedList.printLinkList();
Node reversedHeadNode = singleLinkedList.reverseLinkedList();
System.out.println("**************链表反转之后********************");
while (reversedHeadNode != null){
System.out.print(reversedHeadNode.data+"\t");
reversedHeadNode = reversedHeadNode.next;
}
System.out.println();
}
/**
* 查找数据第一次在链表中出现的位置
*/
@Test(priority = 12)
public void findNodeIndex() {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.tailAddNode(9);
singleLinkedList.tailAddNode(2);
singleLinkedList.tailAddNode(8);
singleLinkedList.tailAddNode(13);
singleLinkedList.tailAddNode(10);
singleLinkedList.tailAddNode(6);
Assert.assertEquals(singleLinkedList.findNodeIndex(singleLinkedList.head,13),4);
}
/**
* 测试插入
*/
@Test(priority = 13)
public void insertDataToLinkedListByIndex(){
SingleLinkedList singleLinkedList = new SingleLinkedList();
System.out.println("****************插入之前***链表为空****0******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前***链表为空****0******************");
singleLinkedList.insertDataToLinkedListByIndex(66,-2);
System.out.println("****************插入之后***链表为空,插入位置为负****0******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后***链表为空,插入位置为负****0******************");
System.out.println("****************插入之前***链表为空****1******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前****链表为空***1******************");
singleLinkedList.insertDataToLinkedListByIndex(1,2);
System.out.println("****************插入之后*****index>length****1****************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后****index>length*****1****************");
System.out.println("****************插入之前*****链表不为空**2******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前******链表不为空*2******************");
singleLinkedList.insertDataToLinkedListByIndex(23,6);
System.out.println("****************插入之后***index>length****index>length**2****************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后***index>length****index>length**2****************");
System.out.println("****************插入之前*******3******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前*******3******************");
singleLinkedList.insertDataToLinkedListByIndex(46,2);
System.out.println("****************插入之后***插到尾结点位置******3****************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后***插到尾结点位置******3****************");
System.out.println("****************插入之前*******4******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前*******4******************");
singleLinkedList.insertDataToLinkedListByIndex(990,1);
System.out.println("****************插入之后***插到头结点位置******4****************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后***插到头结点位置******4****************");
System.out.println("****************插入之前*******5******************");
singleLinkedList.printLinkList();
System.out.println("****************插入之前*******5******************");
singleLinkedList.insertDataToLinkedListByIndex(666,3);
System.out.println("****************插入之后***插到链表中间******5****************");
singleLinkedList.printLinkList();
System.out.println("****************插入之后***插到链表中间******5****************");
}
}
双向链表
双向链表:每个结点不止有一个后继指针next指向后面的结点,还有一个前驱指针prev指向前面的结点
- 添加结点
/**
* 将结点添加到双向链表中
*
* @param newNode 待添加结点
*/
public void add(DoubleNode newNode) {
if (head == null) {
head = new DoubleNode(null);
head.next = newNode;
newNode.pre = head;
return;
}
DoubleNode doubleNode = head;
//找到链表中最后一个结点
while (doubleNode.next != null) {
doubleNode = doubleNode.next;
}
// 将原链表中最后一个结点的后继指针指向要添加的结点
doubleNode.next = newNode;
// 将待添加的结点的前驱指针指向原链表中的最后一个结点
newNode.pre = doubleNode;
}
- 删除结点
/**
* 删除双向链表中值为delData的结点
*
* @param delData
*/
public void deleteNode(int delData) {
DoubleNode doubleNode = head.next;
if (doubleNode == null) {
return;
}
while (doubleNode != null) {
// 找到待删除的结点
if (doubleNode.data == delData) {
doubleNode.pre.next = doubleNode.next;
// 尾结点的next指向null,不需要执行以下语句
if (doubleNode.next != null) {
doubleNode.next.pre = doubleNode.pre;
}
}
doubleNode = doubleNode.next;
}
}
双向链表完整代码:
- 结点类
package com.linkedlist.dto;
/**
* @Author: xpengfei
* @Date: 2020/6/14 16:01
* @Description:双向链表结点类
*/
public class DoubleNode {
/**
* 节点数据域,用于存储数据
*/
public Integer data;
/**
* 节点的指针域,前驱指针,用于存储上一个节点的位置
*/
public DoubleNode pre;
/**
* 节点的指针域,后继指针,用于存储下一个节点的位置
*/
public DoubleNode next;
public DoubleNode(Integer data) {
this.data = data;
}
public DoubleNode(int data, DoubleNode pre, DoubleNode next) {
this.data = data;
this.pre = pre;
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DoubleNode getPre() {
return pre;
}
public void setPre(DoubleNode pre) {
this.pre = pre;
}
public DoubleNode getNext() {
return next;
}
public void setNext(DoubleNode next) {
this.next = next;
}
}
- 双向链表操作类
package com.linkedlist;
import com.linkedlist.dto.DoubleNode;
/**
* @Author: xpengfei
* @Date: 2020/6/6 22:25
* @Description:双向链表
*/
public class DoubleLinkedList {
/**
* 双向链表头结点
*/
protected DoubleNode head;
/**
* 将结点添加到双向链表中
*
* @param newNode 待添加结点
*/
public void add(DoubleNode newNode) {
if (head == null) {
head = new DoubleNode(null);
head.next = newNode;
newNode.pre = head;
return;
}
DoubleNode doubleNode = head;
//找到链表中最后一个结点
while (doubleNode.next != null) {
doubleNode = doubleNode.next;
}
// 将原链表中最后一个结点的后继指针指向要添加的结点
doubleNode.next = newNode;
// 将待添加的结点的前驱指针指向原链表中的最后一个结点
newNode.pre = doubleNode;
}
/**
* 删除双向链表中值为delData的结点
*
* @param delData
*/
public void deleteNode(int delData) {
DoubleNode doubleNode = head.next;
if (doubleNode == null) {
return;
}
while (doubleNode != null) {
// 找到待删除的结点
if (doubleNode.data == delData) {
doubleNode.pre.next = doubleNode.next;
// 尾结点的next指向null,不需要执行以下语句
if (doubleNode.next != null) {
doubleNode.next.pre = doubleNode.pre;
}
}
doubleNode = doubleNode.next;
}
}
/**
* 打印出链表中的所有节点
*/
public void printLinkList() {
System.out.println("--------------------链表输出开始----------------------");
DoubleNode temp = head;
while (temp != null) {
System.out.print(temp.data + "\t");
temp = temp.next;
}
System.out.println();
System.out.println("--------------------链表输出结束----------------------");
}
}
- 双向链表操作测试类
package com.linkedlist;
import com.linkedlist.dto.DoubleNode;
import org.testng.annotations.Test;
/**
* @Author: xpengfei
* @Date: 2020/6/14 16:48
* @Description:双向链表测试类
*/
public class DoubleLinkedListTest {
/**
* 测试添加结点
*/
@Test(priority = 1)
public void add() {
DoubleNode node = new DoubleNode(66);
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.add(node);
node = new DoubleNode(99);
doubleLinkedList.add(node);
node = new DoubleNode(233);
doubleLinkedList.add(node);
doubleLinkedList.printLinkList();
}
/**
* 测试删除结点
*/
@Test(priority = 2)
public void deleteNode(){
DoubleNode node = new DoubleNode(66);
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.add(node);
node = new DoubleNode(99);
doubleLinkedList.add(node);
node = new DoubleNode(233);
doubleLinkedList.add(node);
node = new DoubleNode(456);
doubleLinkedList.add(node);
node = new DoubleNode(789);
doubleLinkedList.add(node);
doubleLinkedList.printLinkList();
System.out.println("***************删除头结点66*******************");
doubleLinkedList.deleteNode(66);
doubleLinkedList.printLinkList();
System.out.println("***************删除尾结点789*******************");
doubleLinkedList.deleteNode(789);
doubleLinkedList.printLinkList();
System.out.println("***************删除中间结点233*******************");
doubleLinkedList.deleteNode(233);
doubleLinkedList.printLinkList();
}
}
循环链表
循环链表与单链表的区别在于:单链表的尾结点指针指向空地址,而循环链表的尾结点指针指向链表的头结点。
- 优点:从链尾到链头比较方便,适合处理具有环形结构特点的数据。
- 循环链表添加结点
/**
* 添加结点
*
* @param newNode 待添加结点
*/
public void addNode(CircleNode newNode) {
if (head == null) {
head = new CircleNode(null);
tail = new CircleNode(null);
head.setNext(newNode);
newNode.setNext(tail);
tail.setNext(head);
}
CircleNode circleNode = head;
// 链表中无数据,只有首尾结点时
if (circleNode.getNext().getNext() == head) {
circleNode.setNext(newNode);
newNode.setNext(tail);
return;
}
//找到尾结点前的一个节点
while (circleNode.getNext().getNext() != head) {
circleNode = circleNode.getNext();
}
circleNode.setNext(newNode);
newNode.setNext(tail);
}
- 循环链表删除结点
/**
* 删除循环链表中值为delData的结点
*
* @param delData
*/
public void deleteNode(int delData) {
CircleNode circleNode = head;
// head结点的下一个结点指向head,链表为空
if (circleNode.getNext() == head) {
return;
}
// 结点指针域指向head结点,则链表遍历结束
while (circleNode.getNext().getNext() != head) {
// 找到待删除数据的上一个结点
if (circleNode.getNext().getData() == delData) {
circleNode.setNext(circleNode.getNext().getNext());
return;
}
circleNode = circleNode.getNext();
}
}
总结
栈
栈是一种只能在一端进行插入或删除操作的线性表,其中允许进行插入或删除的一端称为栈顶。
顺序栈
- 顺序栈实现:
package com.stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
/**
* @Author: xpengfei
* @Date: 2020/6/25 9:28
* @Description:数组实现顺序栈
*/
public class ArrayStack<T> {
private static final Logger logger = LoggerFactory.getLogger(ArrayStack.class);
/**
* 存放栈元素的数组
*/
private T[] arrayStack;
/**
* 栈中元素个数
*/
private int n = 0;
/**
* 无参构造方法,初始化栈数组大小为10
*/
public ArrayStack(Class<T> type) {
arrayStack = (T[]) Array.newInstance(type, 10);
logger.info("栈初始容量为:" + 10);
}
/**
* 构造方法,指定栈数组大小
*
* @param size
*/
public ArrayStack(Class<T> type, int size) {
this.arrayStack = (T[]) Array.newInstance(type, size);
logger.info("栈初始容量为:" + size);
}
/**
* 调整栈数组的大小
*
* @param maxSize
*/
private void reSize(Class<T> type, int maxSize) {
// 通过 Array.newInstance()方法实现Java中数组的泛型
T[] temp = (T[]) Array.newInstance(type, maxSize);
for (int i = 0; i < arrayStack.length; i++) {
temp[i] = arrayStack[i];
}
arrayStack = temp;
logger.info("******************栈已扩容为原来的2倍:" + 2 * n + "*******************");
}
/**
* 判断栈是否为空
*
* @return true:空 false:非空
*/
public boolean isEmpty() {
return n == 0;
}
/**
* 入栈:向栈顶插入元素
*
* @param item
*/
public void push(Class<T> type, T item) {
// 栈满时,自动扩容为运来的两倍
if (n == arrayStack.length) {
reSize(type, n * 2);
}
arrayStack[n] = item;
//栈元素总数+1
n++;
}
/**
* 出栈:从栈顶移除元素并将元素返回
*
* @return
*/
public T pop() {
// 栈空则返回null
if (isEmpty()) {
return null;
}
T temp = arrayStack[n - 1];
arrayStack[n - 1] = null;
// 栈元素个数减一
n--;
return temp;
}
/**
* 返回栈大小
*
* @return
*/
public int size() {
return n;
}
/**
* 输出栈元素
*/
public void printStack() {
logger.info("---------------栈元素输出开始----------------");
if (size() == 0) {
logger.info("栈空!");
} else {
for (int i = 0; i < n; i++) {
logger.info(arrayStack[i] + "\t");
}
logger.info("");
}
logger.info("---------------栈元素输出结束----------------");
}
}
测试出栈过程:
数组实现顺序栈过程中,遇到Java泛型问题,定义数组时使用泛型如
private T[] arrayStack;
在赋值时会报错java.lang.arraystoreexception,通过Array.newInstance()方法在Java中实现数组的泛型
链式栈
- 链式栈实现:
package com.stack;
import com.stack.dto.LinkedListNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author: xpengfei
* @Date: 2020/6/25 9:28
* @Description:链表实现链式栈
*/
public class LinkedListStack<T> {
private static final Logger logger = LoggerFactory.getLogger(LinkedListStack.class);
/**
* 头结点
*/
private LinkedListNode<T> head;
/**
* 栈中元素个数
*/
private int n = 0;
/**
* 头插法,入栈
*
* @param newData 待插入的数据
*/
public void push(T newData) {
// 临时结点指向原链表头结点
LinkedListNode<T> oldHead = head;
// 将newData结点赋给头结点head
head = new LinkedListNode(newData);
// 将新的头结点指向原头结点
head.setNext(oldHead);
// 栈元素+1
n++;
}
/**
* 栈顶元素出栈
*/
public T pop() {
// 栈空,返回null
if (isEmpty()) {
return null;
}
LinkedListNode<T> tempNode = head;
// 删除出栈的结点
head = head.getNext();
// 栈中元素个数减一
n--;
return tempNode == null ? null : tempNode.getData();
}
/**
* 判断栈是否为空
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}
/**
* 返回栈大小(栈中元素个数)
*
* @return
*/
public int size() {
return n;
}
/**
* 输出链式栈
*/
public void printLinkedListStack(){
LinkedListNode<T> tempNode = head;
logger.info("*****************栈元素输出开始*********************");
while (tempNode != null) {
System.out.print(tempNode.getData()+"\t");
tempNode = tempNode.getNext();
}
System.out.println();
logger.info("*****************栈元素输出结束*********************");
}
}
队列
- 队列的特点是先进先出,生活中队列的例子比比皆是,如:平时排队的场景
链式队列
- 链式队列实现:
package com.queue.dto;
/**
* @Author: xpengfei
* @Date: 2020/6/26 14:50
* @Description:链式队列节点类
*/
public class LinkedListQueueDto<T> {
/**
* 数据域
*/
private T data;
/**
* 链式队列指针域
*/
private LinkedListQueueDto<T> next;
public LinkedListQueueDto(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public LinkedListQueueDto<T> getNext() {
return next;
}
public void setNext(LinkedListQueueDto<T> next) {
this.next = next;
}
}
package com.queue;
import com.queue.dto.LinkedListQueueDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author: xpengfei
* @Date: 2020/6/26 10:25
* @Description:链式队列
*/
public class LinkedListQueue<T> {
private static final Logger logger = LoggerFactory.getLogger(LinkedListQueue.class);
/**
* 链式队列中结点个数
*/
private int n = 0;
/**
* 链式队列头结点
*/
private LinkedListQueueDto<T> head;
/**
* 链式队列尾结点
*/
private LinkedListQueueDto<T> rear;
/**
* 入队,链表尾部入队
*
* @param data 待入队数据
*/
public void enqueue(T data) {
LinkedListQueueDto<T> newNode = new LinkedListQueueDto<>(data);
// 队列为空时,直接在首结点后插入
if (isEmpty()) {
head = new LinkedListQueueDto<>(null);
head.setNext(newNode);
// 将新节点指向尾结点
newNode.setNext(rear);
} else {
LinkedListQueueDto<T> tempNode = head;
//找到队列中尾结点前一个节点
while (tempNode.getNext() != null) {
tempNode = tempNode.getNext();
}
//新节点插入
tempNode.setNext(newNode);
// 将新节点指向尾结点
newNode.setNext(rear);
}
// 队列中节点数+1
n++;
}
/**
* 出队,链式队列队头出队
*
* @return
*/
public T dequeue() {
if (isEmpty()) {
return null;
}
LinkedListQueueDto<T> tempNode = head.getNext();
head.setNext(head.getNext().getNext());
n--;
return tempNode.getData();
}
/**
* 判断队列是否为空
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}
/**
* 获取队列中数据结点个数
*
* @return
*/
public int size() {
return n;
}
/**
* 输出链式队列
*/
public void printLinkedListQueue() {
LinkedListQueueDto<T> tempNode = head.getNext();
logger.info("--------------------链式队列输出开始---------------------");
while (tempNode != null) {
System.out.print(tempNode.getData() + "\t");
tempNode = tempNode.getNext();
}
System.out.println();
logger.info("--------------------链式队列输出结束---------------------");
}
}
顺序队列
- 顺序队列实现:
package com.queue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
/**
* @Author: xpengfei
* @Date: 2020/6/26 16:46
* @Description:数组实现队列
*/
public class ArrayQueue<T> {
private static final Logger logger = LoggerFactory.getLogger(ArrayQueue.class);
/**
* 数组队列
*/
private T[] queue;
/**
* 数组大小
*/
private int size;
/**
* 数组中最后一个元素的下标,-1:队列为空
*/
private int rearIndex = -1;
/**
* 构造函数初始化数组
*
* @param type
* @param size
*/
public ArrayQueue(Class<T> type, int size) {
if (size <= 0) {
logger.error("数组大小不能小于0!");
return;
}
this.size = size;
queue = (T[]) Array.newInstance(type, size);
}
/**
* 判断队列是否为空
*
* @return
*/
public boolean isEmpty() {
return rearIndex == -1;
}
/**
* 判断队列是否已满
*
* @return
*/
public boolean isFull() {
return rearIndex == size - 1;
}
/**
* 入队
*
* @param data
* @return
*/
public boolean enqueue(T data) {
if (isFull()) {
logger.warn("队列已满!" + data + "入队失败");
return false;
}
queue[++rearIndex] = data;
return true;
}
/**
* 出队
*
* @return
*/
public T dequeue() {
// 队空
if (isEmpty()) {
logger.error("队空!");
return null;
} else {
// 待出队数据
T temp = queue[0];
//将数组中数据向前移动一位
for (int i = 0; i <= rearIndex - 1; i++) {
queue[i] = queue[i + 1];
}
rearIndex--;
return temp;
}
}
/**
* 输出数组队列
*/
public void printQueue() {
if (isEmpty()) {
logger.info("队列为空!");
return;
}
logger.info("---------------------数组队列输出开始--------------------");
for (int i = 0; i <= rearIndex; i++) {
System.out.print(queue[i] + "\t");
}
System.out.println();
logger.info("---------------------数组队列输出结束--------------------");
}
}
ps:文章仅作记录,水平有限,如有错误之处,欢迎交流指正~