文章目录
1.单链表的性质及其优、缺点
2.不带头节点的链表的插入删除操作
3.带头节点的单链表
4.不带头节点的链表的基础操作:
(1)插入(头插法与尾插法)
(2)删除
(3)链表的逆置
(4)获取链表倒数第K个节点的值
(5)判断链表是否有环
(6)合并两个有序链表
1.单链表的性质及其优、缺点
性质:
1.链表是以节点的方式来存储的
2.每个节点包含data域和next域
3.链表的各个节点不一定是连续存储的
4.链表分为带头节点的链表和不带头节点的链表
优点:
1.单链表的使用使得内存的使用效率提高,在存储的数据量较大时,不需要大片连续的内存空间
2.数据的插入、删除操作的时间复杂度时O(1),常量时间,速度非常快
缺点:
1.链表的搜索操作需要遍历链表,时间为线性时间O(n),数据量大的时候,搜索效率太低
2.不带头节点的链表
不带头接节点的单链表的基础操作如下:
(1)插入(头插法与尾插法)
(2)删除
不带头节点的单链表的代码实现:
package unheadlink;
public class LinkedTest {
/**
* 单链表的节点类型
*/
private static class Entry<T>{
T data;//单链表的数据域
Entry<T> next;//下一个节点的地址
public Entry(T data, Entry<T> next) {
this.data = data;
this.next = next;
}
}
/**
* 不带头节点的单链表的实现
*/
static class Link<T>{
public Entry<T> head;//指向单链表的第一个节点
public Link() {
this.head=null;
}
/**
* 单链表的头插法
*/
public void insertHead(T val){
Entry<T> newNode = new Entry<>(val,this.head);
this.head = newNode;
}
/**
* 单链表的尾插法
*/
public void insertTail(T val){
Entry<T> newNode = new Entry<T>(val,null);
//头节点为空时,直接插入
if (this.head==null){
head = newNode;
return;
}
//头节点不为空
Entry<T> cur = head; //指针指向头节点
while (cur.next != null){ //用指针遍历链表,直到找到链表的最后一个节点
cur=cur.next;
}
cur.next=newNode;//找到了最后一个节点,将新节点插入
}
}
public static void main(String[] args) {
Link<Integer> link = new Link<>();
link.insertHead(10);
link.insertHead(20);
link.insertHead(15);
link.show();
System.out.println();
link.insertTail(20);
link.insertTail(26);
link.insertTail(31);
link.show();
System.out.println();
link.remove(20);
link.remove(26);
link.remove(31);
link.show();
System.out.println();
}
}
3.带头节点的单链表
不带头节点的链表的基础操作:
(1)插入(头插法与尾插法)
(2)删除
(3)链表的逆置
(4)获取链表倒数第K个节点的值
(5)判断链表是否有环
(6)合并两个有序链表
/*
不带头节点的单链表的实现
*/
/**
* 单链表的节点类型
*/
static class Entry<T>{
T data;//单链表的数据域
Entry<T> next;//下一个节点的地址
public Entry(T data, Entry<T> next) {
this.data = data;
this.next = next;
}
}
/**
* 单链表的头节点类型
*/
static class HeadEntry<T> extends Entry<T>{
int cnt;//用来记录节点个数
public HeadEntry(int cnt,T data, Entry<T> next) {
super(data, next);
this.cnt=cnt;
}
}
/**
* 带头节点的单链表实现
*/
class Link<T extends Comparable<T>>{
//指向链表的第一个节点
HeadEntry<T> head;
public Link() {
this.head = new HeadEntry<T>(0,null,null);
}
/**
* 单链表的头插法
*/
public void insertHead(T val){
Entry<T> node = new Entry<>(val,this.head.next);
this.head.next = node;
this.head.cnt +=1;//更新头节点中,节点的数量
}
/**
* 单链表的尾插法
* @param val
*/
public void insertTail(T val){
Entry<T> node = head;
while (node.next!=null){
node = node.next;
}
node.next = new Entry<>(val,null);
this.head.cnt +=1; //跟新头节点中节点个数
}
/**
* 单链表中删除值为val的值
*/
public void remove(T val){
Entry<T> pre = head;
Entry<T> cur = head.next;
while (cur!=null){
if (cur.data==val){
//刚好初始cur指针指向的是要删除的val,删除val
pre.next=cur.next;
this.head.cnt +=1;//更新头节点中链表节点的个数
//break; 只删除第一个值尾val的节点
cur = pre.next; // 删除链表中所有值为val的节点
}
else {
pre = cur;
cur =cur.next;
}
}
}
/**
* 打印单链表所有元素的值
*/
public void show(){
Entry<T> temp = this.head.next;
while (temp!=null){
System.out.println(temp.data+" ");
temp = temp.next;
}
System.out.println();
}
/**
* 获取链表节点 的数目
* @return
*/
public int getNodeCount() {
return this.head.cnt;
}
/**
* 单链表的逆置
* 逆置思想概述:从链表的第二个节点开始,采用头插法
*/
public void reverse(){
if(this.head.next==null){
return;
}
Entry<T> cur = this.head.next.next;//首先用cur指针指向第一个需要进行头插的节点
this.head.next.next = null; //将其置为null
Entry<T> post = null;
while (cur!=null){
post=cur.next; //用一个指针指向下一个待头插的节点
cur.next = head.next;//1.head.next = cur 2.cur = post=cur.next ==>cur.next = head.next
head.next = cur;
cur = post;
}
}
/**
* 获取倒数第K个单链表节点的值
* cur1 cur2
* @param k
* @return
*/
public T getLastK(int k){
Entry<T> cur1 = this.head.next;
Entry<T> cur2 = this.head;
//cur1指向正数第一个节点 cur2指向正数第K个节点
for (int i=0;i<k;i++){
cur2 = cur2.next;
if (cur2==null){
return null;
}
}
// 当cur2到达末尾节点时,cur1指向的就是倒数第k个节点
while (cur2.next!=null){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1.data;
}
}
/**
* 判断链表是否有环,如果有,返回入环节点的值,没有环,返回null
* 入环的节点特征:
* @return
*/
public T getLinkCircleVal(){
//使用快慢指针来解决此类问题
Entry<T> slow = this.head.next;
Entry<T> fast = this.head.next;
while (fast!=null&&fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if (slow==fast){
break;
}
}
if (fast==null){
return null;
} else {
// fast从第一个节点开始走,slow从快慢指针相交的地方开始走,
// 它们相遇的时候,就是环的入口节点
fast = this.head.next;
while (fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow.data;
}
}
/**
* 合并两个有序的单链表
* @param link
*/
public void merge(Link<T> link){
Entry<T> p = this.head;
Entry<T> p1 = this.head.next;
Entry<T> p2 = this.head.next;
// 比较p1和p2节点的值,把值小的节点挂在p的后面
while (p1!=null && p2!=null){
if (p1.data.compareTo(p2.data)>0){
p.next = p2;
p2=p2.next;
}else {
p.next = p1;
p1 = p1.next;
}
p = p.next;
}
if(p1 != null){ // 链表1还有剩余节点
p.next = p1;
}
if(p2 != null){ // 链表2还有剩余节点
p.next = p2;
}
}