线性表(List):零个或者多个具有相同类型的数据元素的有限序列。
若将线性表记为(a1,…,ai-1,ai,ai+1,…,an)的话。则表中ai-1领先于ai,而ai领先于ai+1。故称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。如图所示。
n=0时候,称为空表。
同时,在较为复杂的线性表中,一个数据元素可以由若干个数据项组成。就如下表一样
他们必须要有相同的数据类型。
同时,再聊一聊线性表的抽象数据类型。个人理解,就是对线性表的基本操作。
主要操作
~删除:移除并返回表中指定位置的元素
~插入:插入一个元素到表中
辅助操作
~删除表:清空表中所有元素
~计数:返回表中的元素个数
~查找:寻找从表表尾开始的第n个结点(node)
不多BB,先上线性表的两种结构的分类图
这里值得注意一下:线性表 != 链表, 或者说的是 链表属于线性表的一种分类。
指的是用一段地址连续的存储单元依次存储线性表的数据元素。通常使用数组来实现。
数组长度是固定的,线性表的长度不固定,线性表的当前长度不得大于数组长度,否则会报错。或者动态扩容,但是这样操作会有性能上的损耗。
为了代码方便,这里直接将数据类型定义为int类型
//插入操作
//删除操作
//查找操作
好了,写到这里,因为顺序存储结构两个元素的存储位置具有“相邻”的邻居关系,所以需要链表来解决。
该讲讲接下来的重点:链表。
链表同行指的是单向链表,包含多个结点,每个结点都有指向后继元素的next指针(下一个指针)。最后一个结点指针值为Null,代表着链表的结束。
链表的类型声明代码:
public class ListNode{
private int data;
private ListNode next;
public ListNode(int data){
this.data = data;
}
//setter,getter方法
}
沿着指针遍历,遍历时候显示结点的内容,next指针值为null时候则结束遍历。
下面代码为统计个数。
int ListLength(ListNode headNode){
int length = 0;
ListNode currentNode = headNode;
while(currentNode!= null){
length++;
System.out.pritln("data>>>>>>"+currentNode.getData());
currentNode = currentNode.getNext();
}
}
@Test
public void test(){
ListNode listNode = new ListNode();
ListNode listNode2 = new ListNode();
listNode.setData(111);
listNode2.setData(222);
listNode.setNext(listNode2);
listNode2.setNext(null);
System.out.println("listLength>>>>>>>>>>>>>"+ListLength(listNode));
}
// 输出结果为
//data>>>>111
//data>>>>222
//listLgenth>>>>>>2
思路如下
ListNode InsertInLinkedList(ListNode headNode, ListNode nodeToInsert, int position){
//链表为空,直接插入
if(headNode==null){
return nodeToInsert;
}
int size = ListLength(headNode);
//判断插入位置合法与否
if(position>size+1 ||position<1){
System.out.println("插入位置不合法,合法插入位置是 1到"+(size+1));
return headNode;
}
//在链表开头插入
if(position ==1){
nodeToInsert.setNext(headNode);
return nodeToInsert;
}else {
//在中间或者末尾插入
ListNode previousNode = headNode;
int count = 1;
//知道位置在哪里插入,获得到插入位置的前一个结点
while (count< position-1){
previousNode = previousNode.getNext();
count++;
}
//获得插入结点的后一个结点
ListNode currentNode = previousNode.getNext();
//需要插入的结点的下一个结点自然而然就是currentNode
nodeToInsert.setNext(currentNode);
//同理
previousNode.setNext(nodeToInsert);
}
return headNode;
}
ListNode deleteFromLinkedList(ListNode headNode, int position){
int size = ListLength(headNode);
//判断删除位置合法与否
if(position>size ||position<1){
System.out.println("插入位置不合法,合法插入位置是 1到"+(size));
return headNode;
}
//第一个删除
if(position ==1){
ListNode currentNode = headNode.getNext();
headNode = null;
return currentNode;
}else{
//结尾或中间删除
ListNode preNode = headNode;
int count = 1;
while (count < position -1 ){
preNode = preNode.getNext();
count++;
}
//这个就是被删的了
ListNode currentNode = preNode.getNext();
preNode.setNext(currentNode.getNext());
currentNode=null;
}
return headNode;
}
@Test
public void testDeleteOne(){
ListNode listNode1 = new ListNode();
ListNode listNode2 = new ListNode();
ListNode listNode3 = new ListNode();
ListNode listNode4 = new ListNode();
listNode1.setData(11111);
listNode2.setData(22222);
listNode3.setData(33333);
listNode4.setData(44444);
listNode1.setNext(listNode2);
listNode2.setNext(listNode3);
listNode3.setNext(listNode4);
listNode4.setNext(null);
// System.out.println(deleteFromLinkedList(listNode1, 1));
// System.out.println(">>>>>>>>>>>");
System.out.println(listNode1);
System.out.println(deleteFromLinkedList(listNode1, 2));
System.out.println(">>>>>>>>>>>>>");
}
ListNode findNode(int position,ListNode headNode){
if(headNode==null){
System.out.println("链表为空");
return null;
}
int size = ListLength(headNode);
if(position<1 || position>size){
System.out.println("位置不合法,合法位置是 1到"+(size));
return null;
}
int count =1;
ListNode currentNode = headNode;
while(count< position){
currentNode = currentNode.getNext();
count++;
}
return currentNode;
}
@Test
public void testFindNode(){
ListNode listNode1 = new ListNode();
ListNode listNode2 = new ListNode();
ListNode listNode3 = new ListNode();
ListNode listNode4 = new ListNode();
listNode1.setData(11111);
listNode2.setData(22222);
listNode3.setData(33333);
listNode4.setData(44444);
listNode1.setNext(listNode2);
listNode2.setNext(listNode3);
listNode3.setNext(listNode4);
listNode4.setNext(null);
System.out.println(findNode(1,listNode1));
System.out.println(findNode(2,listNode1));
}
故名思议,双向链表,就是双向的链表。
他的主要优点是:
主要的缺点也是有的:
类型声明代码
public class DLLNode{
private int data;
private DLLNode next;
private DLLNode previous;
private DLLNode(int data){
this.data = data;
}
@Override
public String toString() {
return "DLLNode{" +
"data=" + data +
", next=" + next +
'}';
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DLLNode getNext() {
return next;
}
public void setNext(DLLNode next) {
this.next = next;
}
public DLLNode getPrevious() {
return previous;
}
public void setPrevious(DLLNode previous) {
this.previous = previous;
}
}
int getDLLNodeLength(DLLNode headNode){
int length = 0;
DLLNode currentNode = headNode;
while(currentNode!=null){
length++;
currentNode = currentNode.getNext();
}
return length;
}
@Test
public void testDLLNOdeLength(){
DLLNode node1 = new DLLNode(111);
DLLNode node2 = new DLLNode(222);
DLLNode node3 = new DLLNode(333);
DLLNode node4 = new DLLNode(444);
node1.setPrevious(null);
node1.setNext(node2);
node2.setPrevious(node1);
node2.setNext(node3);
node3.setPrevious(node2);
node3.setNext(node4);
node4.setPrevious(node3);
node4.setNext(null);
System.out.println(getDLLNodeLength(node1));//4
}
DLLNode DLLNodeInsert(DLLNode headNode,DLLNode needToInsertNode, int position){
if(headNode ==null){
return needToInsertNode;
}
int size = getDLLNodeLength(headNode);
System.out.println("链表长度="+size);
if(size+1<position || position<1){
System.out.println("插入位置错误,可插入的位置为1到"+(size+1));
return headNode;
}
if(position==1){
needToInsertNode.setPrevious(null);
needToInsertNode.setNext(headNode);
headNode.setPrevious(needToInsertNode);
return needToInsertNode;
}else {
DLLNode preNode = headNode;
int count =1 ;
while(count<position-1){
preNode = preNode.getNext();
count++;
}
DLLNode currentNode = preNode.getNext();
needToInsertNode.setNext(currentNode);
if(currentNode!=null){
currentNode.setPrevious(needToInsertNode);
}
preNode.setNext(needToInsertNode);
needToInsertNode.setPrevious(preNode);
}
return headNode;
}
DLLNode DLLNodeDelete(DLLNode headNode,int position){
int size = getDLLNodeLength(headNode);
if(position>size || position<1){
System.out.println("Invaild size,the right size should be 1 to "+size);
return headNode;
}
if(position==1){
DLLNode currentNode = headNode.getNext();
currentNode.setPrevious(null);
headNode= null;
return currentNode;
}else {
DLLNode previousNode = headNode;
int count = 1;
if(count<position-1){
count++;
previousNode =previousNode.getNext();
}
//得出被删除的结点
DLLNode currentNode = previousNode.getNext();
//如果被删除的结点不是最后一个的话,即是中间
if(currentNode.getNext()!=null){
//让被删除结点的前驱结点 指向 被删除结点的后驱结点
previousNode.setNext(currentNode.getNext());
//被删除结点的后驱结点也指向被删除的前驱结点
currentNode.getNext().setPrevious(previousNode);
currentNode=null;
}else {
previousNode.setNext(null);
currentNode=null;
}
}
return headNode;
}
结构代码又如单向链表一样
public class CircularList{
private int data;
private CircularList next;
@Override
public String toString() {
return "CircularList{" +
"data=" + data +
", next=" + next +
'}';
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public CircularList getNext() {
return next;
}
public void setNext(CircularList next) {
this.next = next;
}
}
int findCircularListLength(CircularList headNode){
int length = 0;
CircularList currentNode = headNode;
while(currentNode!=null){
length++;
currentNode = currentNode.getNext();
if(currentNode==headNode){
break;
}
}
return length;
}
void insertIntoEndCLL(CircularList headNode,CircularList needToInsertNode){
CircularList currentNode = headNode;
while(currentNode.getNext()!=headNode){
currentNode.setNext(currentNode.getNext());
}
//容易理解,当currentNode.getNext==headNode时,currentNode就是尾结点了
needToInsertNode.setNext(needToInsertNode);
if(headNode==null){
headNode =needToInsertNode;
}else {
needToInsertNode.setNext(headNode);
currentNode.setNext(needToInsertNode);
}
}
void insertIntoBeginCLL(CircularList headNode,CircularList needToInsertNode){
CircularList currentNode = headNode;
while(currentNode.getNext()!=headNode){
currentNode.setNext(currentNode.getNext());
}
needToInsertNode.setNext(needToInsertNode);
if(headNode==null){
headNode =needToInsertNode;
}else {
needToInsertNode.setNext(headNode);
currentNode.setNext(needToInsertNode);
//直接把头结点变成需要插入的即可了。。。
headNode=needToInsertNode
}
void deleteLastNodeFromCircularList(CircularList headNode){
CircularList temp = headNode;
CircularList currentNode = headNode;
if(headNode==null){
System.out.println("链表为空");
return;
}
while (currentNode.getNext()!=headNode){
temp = currentNode;
currentNode = currentNode.getNext();
}
temp.setNext(headNode);
currentNode = null;
return;
}
void deleteFrontNodeFromDLL(CircularList headNode){
CircularList temp = headNode;
CircularList current = headNode;
if(headNode==null){
System.out.println("List Empty");
return;
}
while (current.getNext()!=headNode)
current.setNext(current.getNext());
current.setNext(headNode.getNext());
headNode = headNode.getNext();
temp = null;
return;
}
顺序存储(数组)的优点:
顺序存储(数组)的缺点:
链式存储(链表)的优点:
链式存储(链表)的缺点:
本人学习的理解笔记,如有错误!不吝赐教,请留言,谢谢你!