前言唠叨一下
最近有点时间,总结了一下单链表的各种操作,包括增删改查,就地逆置、求倒数第k个节点等。工作之余重新回顾一下,虽然工作中很少用到,Java也封装了比较完善的链表实现,但对于自己的算法理解多多少少也有所帮助。
效果图
链表类实现逻辑
package com.list;
/**
* 单链表-带头结点
*/
public class SingleLinkedList<T> {
private int size;
private Node head;
/**
* 构造一个空的链表
*/
public SingleLinkedList() {
this.size = 0;
head = new Node();
}
/**
* 创建大小为size的链表,元素默认值为JDK类加载时的默认值
* @param size, 链表大小
*/
public SingleLinkedList(int size) {
this.size = size;
head = createList(size);
}
/**
* 根据传入的数组,构造链表
* @param datas
*/
public SingleLinkedList(T[] datas) {
this.size = datas.length;
head = createList(datas);
}
/**
* 按照传入的数据元素创建链表
* @param datas
*/
private SingleLinkedList<T>.Node createList(T[] datas) {
Node head = this.createList(datas.length);
Node p = head.next;
for (T d : datas) {
p.data = d;
p = p.next;
}
return head;
}
/**
* 创建大小为 size 带有JDK默认初始值的链表
* @param size
*/
private Node createList(int size) {
if(size < 0) new Exception("参数异常");
Node head = new Node();
Node p = head;
for(int i=0; i<size; i++){
p.next = new Node();
p = p.next;
}
return head;
}
/**
* 获取倒数第k个节点值
* @param k 元素位置
*/
public T getBackValue(int k){
if(k <= 0 || k > size){
return null;
}
Node p = head.next;
Node cur = p;
int count = 1;
// cur先走
while(cur.next != null && count++ != k){
cur = cur.next;
}
// 一起走,cur.next走到最后为空时,p为倒数第k个节点
while(cur.next != null){
cur = cur.next;
p = p.next;
}
return p.data;
}
/**
* 在第 pos个位置之前插入data
* @param pos 插入的位置
* @param data 插入元素
* @return 是否插入成功
*/
public boolean insert(int pos, T data){
if(pos <=0 || pos > size) {
// 不在范围内,直接插入在末尾
insertLast(data);
return true;
}
Node p = head;
for(int i=1; i<pos; i++){
p = p.next;
}
// 插入新节点
Node newNode = new Node();
newNode.data = data;
newNode.next = p.next;
p.next = newNode;
size ++ ;
return true;
}
/**
* 移除第 pos个位置元素
* @param pos 元素位置
*/
public void remove(int pos){
if(pos <=0 || pos > size) {
throw new NumberFormatException("number invaild position : " + pos) ;
}
Node p = head;
for(int i=1; i<pos; i++){
p = p.next;
}
// remove
Node q = p.next;
p.next = q.next;
size -- ;
}
/**
* 替换第 pos 号位置的元素为 data
* @param pos 元素位置
* @param data 要替换的元素
*/
public void replace(int pos, T data){
if(pos <=0 || pos > size) {
throw new NumberFormatException("number invaild position : " + pos) ;
}
Node p = head;
for(int i=1; i<pos; i++){
p = p.next;
}
// replace
p.next.data = data;
size -- ;
}
/**
* @param data
* 在末尾插入元素
*/
public void insertLast(T data){
Node p = head;
while(p.next != null){
p = p.next;
}
Node newNode = new Node();
newNode.data = data;
p.next = newNode;
size ++;
}
/**
* 单链表就地逆置-只改变指针,不改变值
*/
public void reversList(){
if(size == 0) return ;
Node p = head.next;
Node r = p.next;
Node pre;
// 第一个节点,next置空
p.next = null;
while(r != null){
pre = p;
p = r;
r = r.next;
p.next = pre;
}
// 头结点归位
head.next = p;
}
/**
* @param sl
* 打印链表
*/
public void printList(){
if(size > 0){
Node p = head;
while(p != null){
if(p.next == null){
System.out.print(p.data );
break;
}
System.out.print(p.data + " -> ");
p = p.next;
}
System.out.println();
}
}
public int getSize() {
return size;
}
public Node getHead() {
return head;
}
class Node{
T data;
Node next;
}
}
测试类
package com.list;
public class Test {
public static void main(String[] args) {
System.err.println("说明: \"null\" 表示头节点 , \"->\"表示两个节点的前驱后继关系。 ");
System.out.println();
System.out.println("第一种方式构造链表:{1, 2, 3, 4, 5}");
Integer[] arr = {1, 2, 3, 4, 5};
SingleLinkedList<Integer> s = new SingleLinkedList<>(arr);
// 打印
s.printList();
System.out.println();
System.out.println("=========== 华丽丽的分割线 ==============");
System.out.println();
System.out.println("第二种方式构造链表:从空链表开始构造");
SingleLinkedList<Integer> list = new SingleLinkedList<>();
System.out.println("分别在第 1, 2, 3,4 号位置插入元素 11, 22, 3344");
list.insert(1, 11);
list.insert(2, 22);
list.insert(3, 33);
list.insert(4, 44);
list.printList();
System.out.println();
System.out.println("将第2个节点替换为7");
list.replace(2, 7);
list.printList();
System.out.println();
Integer v = list.getBackValue(3);
System.out.println("获取倒数第3个节点的值 :" + v);
System.out.println();
int data2 = 0;
System.out.println("在最后一个节点后面插入 : " + data2);
list.insertLast(data2);
list.printList();
System.out.println();
int pos = 2;
System.out.println("移除第" + pos + "个节点 : " + pos);
list.remove(pos);
list.printList();
System.out.println();
System.out.println("反转链表:");
list.reversList();
list.printList();
System.out.println();
}
}
最后说明
使用了泛型,理论上可以支持所有引用类型。多线程下可能会出现线程安全问题。
算法项目代码:https://download.csdn.net/download/yanglong_blog_/12636522