链表简介
什么是链表?
链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等
链表属性
- 相邻元素之间通过指针链接
- 最后一个元素的后继指针为NULL
- 在程序执行过程中,链表的长度可以增加或缩小
- 链表的空间能够按需分配
- 没有内存空间的浪费
优点:
- 插入和删除时不需移动其他元素, 只需改变指针。
- 链表各个节点在内存中空间不要求连续!空间利用率高。
优点:
- 访问数组元素效率低。
单向链表
结构
单向链表的类型声明
public class ListNode {
private int data;
private ListNode next;
public ListNode(int data){
this.data = data;
}
public void setData(int data){
this.data = data;
}
public int getData(){
return data;
}
public void setNext(ListNode next){
this.next = next;
}
public ListNode getNext(){
return this.next;
}
}
遍历单向链表
int ListLength(ListNode headNode){
int length = 0;
ListNode current = headNode;
while(current != null){
length++;
current = current.getNext();
}
return length;
}
时间复杂度为O(n), 空间复杂度O(1)
单向链表插入元素
单向链表插入操作有以下三种情况: 表头插入,表尾插入,中间插入
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("Position of nodeToInsert is invalid");
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();
nodeToInsert.setNext(currentNode);
previousNode.setNext(nodeToInsert);
}
return headNode;
}
时间复杂度为O(n), 空间复杂度O(1)
链表删除元素
单向链表插入操作有以下三种情况:删除表头元素,删除表尾元素, 删除中间元素
ListNode DeleteNodeFromLinkedList(ListNode headNode, int position){
int size = ListLength(headNode);
if(position > size || position < 1){
return headNode;
}
if(position == 1){
ListNode currentNode = headNode.getNext();
headNode = null;
return currentNode;
}else{
ListNode previousNode = headNode;
int count = 1;
while (count < position){
previousNode = previousNode.getNext();
count++;
}
ListNode currentNode = previousNode.getNext();
previousNode.setNext(currentNode.getNext());
currentNode = null;
}
return headNode;
}
时间复杂度为O(n), 空间复杂度O(1)
删除单向链表
void DeleteLinkedList(ListNode headNode){
ListNode auxilaryNode, iterator = headNode;
while (iterator != null){
auxilaryNode = iterator.getNext();
iterator = null;
iterator = auxilaryNode;
}
}
时间复杂度为O(n), 空间复杂度O(1)
双向链表
优点:
对于链表中一个给的的结点,可以从两个方向进行操作。
缺点:
- 每个结点需要再添加一个额外的指针,因此需要更多的空间开销
- 结点的插入或者删除更加费时
结构
双向链表类型声明
public class DLLNode {
private int data;
private DLLNode next;
private DLLNode previous;
public DLLNode(int data){
this.data = data;
}
public void setData(int data){
this.data = data;
}
public int getData(){
return data;
}
public void setNext(DLLNode next){
this.next = next;
}
public DLLNode getNext(){
return this.next;
}
public void setPrevious(DLLNode previous){
this.previous = previous;
}
public DLLNode getPrevious(){
return this.previous;
}
}
双向链表插入元素
单向链表插入操作有以下三种情况:表头插入,表尾插入, 中间插入
DLLNode DLLInsert(DLLNode headNode, DLLNode nodeToInsert, int position) {
// 链表为空时插入
if (headNode == null) {
return nodeToInsert;
}
int size = ListLength(headNode);
if (position > size + 1 || position < 1) {
System.out.println("Position of nodeToInsert is invalid");
return headNode;
}
if (position == 1) { // 在链表开头插入
nodeToInsert.setNext(headNode);
headNode.setPrevious(nodeToInsert);
} else {// 在链表中间或末尾插入
DLLNode previousNode = headNode;
int count = 1;
while (count < position - 1) {
previousNode = previousNode.getNext();
count++;
}
DLLNode currentNode = previousNode.getNext();
nodeToInsert.setNext(currentNode);
if (currentNode != null) {
currentNode.setPrevious(nodeToInsert);
}
previousNode.setNext(nodeToInsert);
nodeToInsert.setPrevious(previousNode);
}
return headNode;
}
时间复杂度为O(n), 空间复杂度O(1)
双向链表删除元素
单向链表插入操作有以下三种情况:删除表头元素,删除表尾元素,删除中间元素
DLLNode DDLDelete(DLLNode headNode, int position){
int size = ListLength(headNode);
// 如果被删除位置不在链表长度范围内,报错并返回
if(position > size || position < 1){
System.out.println("Position of node to delete is invalid.");
}
if(position == 1){ //删除链表的第一个结点
DLLNode currentNode = headNode.getNext();
headNode = null;
currentNode.setPrevious(null);
return currentNode;
}else { //删除中间或表尾结点
DLLNode previousNode = headNode;
int count = 1;
while (count < position - 1){
previousNode = previousNode.getNext();
count++;
}
DLLNode currentNode = previousNode.getNext();
DLLNode laterNode = currentNode.getNext();
previousNode.setNext(laterNode);
if(laterNode != null){
// 如果被删除结点的后继结点不是null结点,那么设置其前驱指针指向被删除结点的前驱结点
laterNode.setPrevious(previousNode);
}
currentNode = null;
}
return headNode;
}
时间复杂度为O(n), 空间复杂度O(1)
循环链表
结构
类型声明(同单向链表)
public class CLLNode {
private int data;
private CLLNode next;
public CLLNode(int data) {
this.data = data;
}
public void setData(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void CLLNode(CLLNode next) {
this.next = next;
}
public CLLNode getNext() {
return this.next;
}
}
循环链表的长度
int CircularListLength(CLLNode headNode) {
int length = 0;
CLLNode currentNode = headNode;
while (currentNode != null){
length++;
currentNode = currentNode.getNext();
if(currentNode == headNode){
break;
}
}
return length;
}