单向链表实现
1.1 基本介绍
链表(linked list)
是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。链表中数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域(data),另一个是存储下一个结点地址的指针域(next)。(百度百科)
1.2 存储原理
链表的每一个节点分布在内存的不同位置,依靠next
指针关联起来。这样可以灵活有效地利用零散的碎片空间。链表的第1
个节点被称为头节点(3)
,没有任何节点的next
指针指向它,或者说它的前置节点为空头结点用来记录链表的基地址。有了它就可以遍历得到整条链表链表的最后1
个节点被称为尾节点(2)
,它指向的nex
t为NULL
。
1.3 添加操作
1、思路分析
- 为了让代码更加精简,统一所有节点的处理逻辑,可以在最前面增加一个虚拟的头结点(不存储数据)
- 新节点的
next
指针,指向插入位置的节点。插入位置前置节点的next
指针,指向新节点。 - 只要内存空间允许,能够插入链表的元素是无限的,不需要像数组那样考虑扩容的问题。
头部添加
当链表为空时,头部插入
当链表不为空时,从头部插入。
先让新的节点指向头结点,然后再让头结点指向新节点!!!
尾部添加
先让尾指针指向的节点,指向新节点
然后tail
从新指向新节点
中间添加
定义一个指针prev
,将prev
的当前下一跳给新节点的下一跳。然后再让prev
节点重新指向新节点
2、代码示例
接口实现:List
package cn.linkedlist.demo01;
/***
* List接口方法
* @param <E>
*/
public interface List<E> extends Iterable<E>{
void add(E element);
void add(int index, E element) ;
void remove(E element);
E remove(int index);
E get(int index);
E set(int index, E element) ;
int size();
int indexOf(E element) ;
boolean contains(E element);
boolean isEmpty();
void clear();
}
链表实现:LinkedSinglyList
package cn.linkedlist.demo01;
import java.util.Iterator;
public class LinkedSinglyList<E> implements List<E>{
// 创建Node节点
private class Node{
//数据域 用来存储数据的
public E data;
//指针域 用来存储下一个结点对象的地址
public Node next;
// 构造方法
public Node() {
this(null, null);
}
public Node(E data) {
this(data, null);
}
public Node(E data, Node next) {
this.data = data;
this.next = next;
}
@Override
public String toString(){
return data.toString();
}
}
// 链表元素的数量
private int size;
//链表当中的头指针指向第一个结点对象
private Node head;
//链表当中的头指针指向最后一个结点对象
private Node tail;
// 初始化链表
public LinkedSinglyList(){
head = null;
tail = null;
size = 0;
}
public LinkedSinglyList(E[] arr){
for (E e : arr){
add(e);
}
}
/***
* 在链表末尾添加新的元素e
* @param element
*/
@Override
public void add(E element) {
add(size, element);
}
/***
* 根据链表的index位置添加新的元素e
* @param index
* @param element
*/
@Override
public void add(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("add index out of bounds");
}
// 创建新的结点对象
Node node = new Node(element);
if(isEmpty()){
// 链表为空
head = node;
tail = node;
}else if(index == 0){
// 在链表头部添加元素
node.next = head;
head = node;
}else if(index == size){
// 在链表尾部添加元素
tail.next = node;
tail = node;
}else{
// 在链表中添加元素
Node prev = head;
for(int i=0; i < index -1; i++){
prev = prev.next;
}
node.next = prev.next;
prev.next = node;
}
size++;
}
@Override
public int size() {
return size;
}
/***
* 判断链表是否为空
* @return
*/
@Override
public boolean isEmpty() {
return size == 0 && head == null && tail == null;
}
@Override
public void clear() {
head = null;
tail = null;
size = 0;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("size=").append(size).append(", [");
// 定义一个指针变量
Node cur = head;
while(cur != null){
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
res.append("]");
return res.toString();
}
/***
* 迭代器实现
* @return
*/
@Override
public Iterator<E> iterator() {
return new LinkedListIterator();
}
class LinkedListIterator implements Iterator<E>{
// 定义游标
private Node cur = head;
@Override
public boolean hasNext() {
return cur != null;
}
@Override
public E next() {
E ret = cur.data;
cur = cur.next;
return ret;
}
}
}
3、测试代码:LinkedSinglyListDemo
package cn.linkedlist.demo01;
public class LinkedSinglyListDemo {
public static void main(String[] args) {
LinkedSinglyList<Integer> linkedList = new LinkedSinglyList<>();
System.out.println("===链表头部插入===");
linkedList.add(0,1);
linkedList.add(0,3);
linkedList.add(0,5);
linkedList.add(0,8);
System.out.println(linkedList);
System.out.println("==链表尾部插入==");
linkedList.add(12);
System.out.println(linkedList);
System.out.println("===链表中间插入===");
linkedList.add(2, 23);
System.out.println(linkedList);
}
}
4、执行结果
1.4 查询和修改
1、查询操作
链表:SingleLinkedList
/***
* 得链表的第index个位置的元素
* @param index
* @return
*/
@Override
public E get(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("get index out of bounds");
}
// 获取头部
if(index == 0){
return head.data;
}else if(index == size -1){
// 获取尾部
return tail.data;
}else{
// 获取中间
Node prev = head;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
return prev.data;
}
}
/***
* 查找元素在链表中第一次出现的索引
* @param element
* @return
*/
@Override
public int indexOf(E element) {
// 判断链表是否为空
if(isEmpty()){
return -1;
}
// 定义prev指针
Node prev = head;
// 定义索引值
int index = 0;
while (!prev.data.equals(element)){
prev = prev.next;
index++;
// 如果没有找到,则返回-1
if(prev == null){
return -1;
}
}
return index;
}
/***
* 查找链表中是否有元素element
* @param element
* @return
*/
@Override
public boolean contains(E element) {
return indexOf(element)!= -1;
}
测试代码:LinkedSinglyListDemo
package cn.linkedlist.demo01;
public class LinkedSinglyListDemo {
public static void main(String[] args) {
LinkedSinglyList<Integer> linkedList = new LinkedSinglyList<>();
System.out.println("===插入元素===");
linkedList.add(0,1);
linkedList.add(0,3);
linkedList.add(0,5);
linkedList.add(0,8);
linkedList.add(12);
linkedList.add(2, 23);
System.out.println(linkedList);
System.out.println("===查找元素===");
Integer integer = linkedList.get(2);
System.out.println("通过索引获取元素:" + integer);
boolean b = linkedList.contains(23);
System.out.println("是否存在该元素:" + b);
int element = linkedList.indexOf(12);
System.out.println("该元素的索引:" + element);
}
}
2、执行结果
3、修改节点
找到要更新的节点,然后把旧数据替换成新数据。
4、代码示例
链表:LinkedSinglyList
/***
* 修改链表中指定index的元素为element
* @param index
* @param element
* @return
*/
@Override
public E set(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("update index out of bounds");
}
// 定义返回值
E result = null;
if(index == 0){
// 修改头部
result= head.data;
head.data = element;
}else if(index == size -1){
// 修改尾部
result = tail.data;
tail.data = element;
}else{
// 修改中间的元素
Node prev = head;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
result = prev.data;
prev.data = element;
}
return result;
}
测试代码:LinkedSinglyListDemo
package cn.linkedlist.demo01;
public class LinkedSinglyListDemo {
public static void main(String[] args) {
LinkedSinglyList<Integer> linkedList = new LinkedSinglyList<>();
System.out.println("===插入元素===");
linkedList.add(0,1);
linkedList.add(0,3);
linkedList.add(0,5);
linkedList.add(0,8);
linkedList.add(12);
linkedList.add(2, 23);
System.out.println(linkedList);
System.out.println("===修改节点元素===");
System.out.println("LinkedSinglyList(修改前)" + linkedList);
linkedList.set(4, 38);
System.out.println("LinkedSinglyList(修改后)" + linkedList);
}
}
5、执行结果
1.5 删除操作
1、思路分析
把要删除节点的前置节点的next
指针,指向要删除元素的下一个节点即可。
删除头结点
head
指向下一个元素
然后让之前的元素断开
删除尾节点
定义一个prev
指针,直接找到尾节点前面那个元素。
让当前prev
的下一个元素,执行置空操作。
然后让尾指针指向该元素
删除中间元素
先找到要删除节点的前驱,让删除节点前一个元素指向要删除元素的后继
然后将要删除节点的后继置空
链表只有一个元素
直接让head
和tail
置空即可
2、代码示例
链表:LinkedSinglyList
/***
* 删除链表中指定的元素element
* @param element
*/
@Override
public void remove(E element) {
int index = indexOf(element);
if(index != -1){
remove(index);
}
}
/***
* 删除链表中指定索引处index的元素
* @param index
* @return
*/
@Override
public E remove(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("remove index out of bounds");
}
// 定义返回值
E result = null;
// 当链表只剩下一个元素
if(size == 1){
result = head.data;
head = null;
tail = null;
}else if(index == 0){
// 删除链表头部
Node prev = head;
result = prev.data;
head = prev.next;
// 置空操作
prev.next = null;
}else if(index == size -1){
// 删除链表尾部
Node prev = head;
while (prev.next != tail){
prev = prev.next;
}
result = tail.data;
// 置空操作
prev.next = null;
tail = prev;
}else{
// 删除中间的某个元素
Node prev = head;
for (int i = 0; i < index -1; i++) {
prev = prev.next;
}
Node deleteNode = prev.next;
result = deleteNode.data;
prev.next = deleteNode.next;
// 置空
deleteNode.next = null;
}
size --;
return result;
}
测试代码:LinkedSinglyListDemo
package cn.linkedlist.demo01;
public class LinkedSinglyListDemo {
public static void main(String[] args) {
LinkedSinglyList<Integer> linkedList = new LinkedSinglyList<>();
System.out.println("===插入元素===");
linkedList.add(0,1);
linkedList.add(0,3);
linkedList.add(0,5);
linkedList.add(0,8);
linkedList.add(12);
linkedList.add(2, 23);
System.out.println(linkedList);
System.out.println("===删除链表节点前====");
System.out.println(linkedList);
System.out.println("===删除链表节点后====");
// 根据链表index位置的元素, 返回删除的元素
linkedList.remove(2);
System.out.println(linkedList);
// 删除链表中的元素
linkedList.remove(0);
System.out.println(linkedList);
}
}
3、执行结果