链表
链表的概念以及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
·单向、双向
·带头、不带头
·循环、非循环
我们主要讲解单向链表
可以将链表看成火车的形式,定义车厢类和火车类,对火车进行相应的操作
链表的实现
public class SingleLinkedList {
private int size;//当前火车中车厢的节点个数
private Node head;//火车头
}
class Node{
int val;//存储具体数据
Node next;//保存下一个车厢的地址
public Node (int val){
this.val = val;
}
}
链表的基本操作
包括链表的增删查改,以及判别某节点是否存在链表中
/**
* 在火车头部添加元素(头插法)
* @param val
*/
public void addFirst(int val){
Node node = new Node(val);//插入节点,先创建车厢
//判断当前的火车是否为空
if(head == null){
head = node;
}else {
node.next = head;
head = node;
}
size++;
}
/**
* 在链表中间位置插入
* 核心:找到待插入位置的前驱结点
* @param index
* @param val
*/
public void addIndex(int index, int val){
//1.合法性
if(index < 0 || index >size){
System.err.println("add index illegal!");
return;
}
if (index == 0){
addFirst(val);
return;
}
//2.插入元素
Node node = new Node(val);
//需要找到待插入的前驱,从头开始向后依次走index-1步
Node prev = head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
node.next = prev.next;
prev.next = node;
size ++;
}
/**
* 尾部插入
* @param val
*/
public void addLast(int val){
addIndex(size,val);
}
/**
* 返回index位置的元素值
* @param index
*/
public int get(int index){
if(rangeCheck(index)){
Node node = head;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node.val;
}else{
System.err.println("get index illegal!");
return -1;
}
}
/**
* 判断链表中是否包含值为val的节点
* @param val
*/
public boolean contains(int val){
for (Node temp = head; temp != null; temp = temp.next) {
if (temp.val == val) {
return true;
}
}
return false;
}
/**
* 将链表中引索为index的节点值改为newVal
* @param index
* @param newVal
* @return修改前的值
*/
public int set(int index,int newVal){
if(rangeCheck(index)){
Node node = head;
for (int i = 0; i < index; i++) {
node = node.next;
}
int oldVal = node.val;
node.val = newVal;
return oldVal;
}else {
System.err.println("get index illegal!");
return -1;
}
}
/**
* 删除index的节点
* @param index
*/
public void removeIndex(int index){
if (rangeCheck(index)){
if(index == 0){
Node temp = head;
head = head.next;
temp.next = null;//弄一个临时变量充当前驱
size --;
}else {
Node prev = head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
//待删除节点
Node cur = prev.next;
prev.next = cur.next;
cur.next = null;
size--;
}
}else {
System.out.println("remove index illegal!");
}
}
public void removeFirst(){
removeIndex(0);
}
public void removeLsat(){
removeIndex(size - 1);
}
/**
* 删除链表中第一个val的值
* @param val
*/
public void removeValueOnce(int val){
//遍历链表,找到值为val的节点
//找到后删除(正常的删除都需要找到前驱,只有头节点没有前驱)
if(head != null && head.val == val){
//头节点就是待删除的节点
Node temp = head;
head = head.next;
temp.next = null;
size--;
}else {
Node prev = head;
//判断前驱的下一个节点是不是val
//看你取值用的是哪个引用,就判断哪个引用不为空
while (prev.next != null){
if(prev.next.val == val){
Node cur = prev.next;//此时cur就是待删除的节点
//删除cur
prev.next = cur.next;
cur.next = null;
size--;
return;
}
//prev不是待删除节点的前驱时
prev = prev.next;
}
}
}
/**
* 删除了链表中所有为val的值
* @param val
*/
public void removeValueAll(int val) {
//此时头节点就是待删除的节点
while (head != null && head.val == val) {
head = head.next;
size--;
}
if (head == null) {
//此时链表中的值全是val
return;
} else {
Node prev = head;
while (prev.next != null) {
if (prev.next.val == val) {
Node cur = prev.next;
prev.next = cur.next;
cur.next = null;
size--;
} else {
prev = prev.next;//重点!!!只有当prev.next不是待删除节点时prev才能移动,必须保证prev不是待删除节点!!
}
}
}
}
/**
* 判断用户输入的inde是否合法(改 查 删除 中使用)
* @param index
* @return
*/
private boolean rangeCheck(int index){
if (index < 0 || index >= size){
return false;
}
return true;
}
public String toString(){
String ret = " ";
//遍历火车这个类
Node temp = head;//创建temp临时变量,避免遍历后head损坏
while (temp != null){
ret +=temp.val;
ret +="->";
temp = temp.next;
}
return ret += "NULL";
}
}
含有虚拟头节点的单链表
经过上面在进行链表删除的时候,会发现在删除过程中比较麻烦,需要找到待删除节点的前驱,就要单独考虑头节点,此时引入虚拟头节点可以解决此问题。
public class SingleLinkedListWithHead {
private int size;
private Node dummyHead = new Node(-1);
public void addIndex(int index,int val){
if(index < 0 || index >size){
System.err.println("add index illegal!");
return;
}
Node node = new Node(val);
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
node.next = prev.next;
prev.next =node;
size++;
}
public void addFirst(int val){
addIndex(0,val);
}
public void addLast(int val){
addIndex(size,val);
}
public void removeIndex(int index){
if(index < 0 || index >size){
System.err.println("add index illegal!");
return;
}
Node prev = dummyHead;
for (int i = 0; i < index ; i++) {
prev = prev.next;
}
//待删除节点
Node cur = prev.next;
prev.next = cur.next;
cur.next = null;
size--;
}
public String toString(){
String ret = " ";
//遍历火车这个类
Node temp = dummyHead.next;//创建temp临时变量,避免遍历后head损坏
while (temp != null){
ret +=temp.val;
ret +="->";
temp = temp.next;
}
return ret += "NULL";
}
}
总结
链表中以节点为单位存储,不支持随机访问
任意位置插入删除时间复杂度为O(1)
没有增容问题,插入一个开辟一个空间
如有疑问欢迎留言~~~~
(来自不会敲代码的卑微贝贝)