1.链表的原理
结点(node): 为了组织链表而引入的一个结构,除了保存我们的元素之外,还会保存指向下一个结点的引用。
1.1代码表示
class Node{
int var; //表示存储的值
Node next; //下一个元素节点的引用
}
1.2链表节点的定义与遍历
//节点类的定义
class Node{
int var;
Node next;
public Node(int var) {
this.var = var;
}
@Override
public String toString() {
return "Node{" +
"var=" + var +
'}';
}
}
public class NodeTest {
//创建一个链表
public static Node CreatNodeList(){
Node a=new Node(1);
Node b=new Node(2);
Node c=new Node(3);
a.next=b;
b.next=c;
c.next=null;
return a;
}
public static void main(String[] args) {
Node head = NodeTest.CreatNodeList();
//遍历链表
while (head!=null){
System.out.println(head);
head=head.next; //更新引用的位置
}
}
}
运行结果:
过程图:
2.java中的链表(LinkedList)
java中的链表不是使用头节点来表示整个链表;而是定义了一个链表类。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
/**
* Constructs an empty list.
*/
public LinkedList() {
}
3.实现自己的(LinkedList)
这里模拟Java实现的一个泛型版本的LinkedList。
class Node<T>{
T var;
Node<T> next;
Node<T> pre;
public Node(T var) {
this.var = var;
}
@Override
public String toString() {
return "Node{" +
"var=" + var +
'}';
}
}
class MyExciption extends RuntimeException{
public MyExciption(String msg) {
super(msg);
}
}
public class MyLinkedList<T> {
private Node<T> head;
private Node<T> Tail;
private int lenght;
public MyLinkedList() {
head=null;
Tail=null;
lenght=0;
}
//获取链表长度的方法
public int getLenght(){
return this.lenght;
}
//头插
public void addHead(T var){
Node<T> newNode=new Node<>(var);
//如果是空链表的情况
if(head==null){
head=newNode;
Tail=newNode;
lenght++;
return;
}
//不是空链表的情况
newNode.next=head;
head.pre=newNode;
head=newNode; //更新头节点引用的位置
lenght++;
return;
}
public void addLast(T var){
Node<T> newNode=new Node<>(var);
//如果是空链表
if(head==null){
head=newNode;
Tail=newNode;
lenght++;
return;
}
//不是空链表的情况
Tail.next=newNode;
newNode.pre=Tail;
Tail=newNode; //更新尾节点引用的位置
lenght++;
return;
}
//在任意位置插入
public void add(T var,int index){
//先判断位置是否非法
if(index<0 || index >lenght){
throw new MyExciption("插入位置非法");
}
if(index==0){ //相当于头插
addHead(var);
return;
}
if(index==lenght){ //相当于尾插
addLast(var);
return;
}
// 1 2 3
Node<T> newNode=new Node<>(var);
Node<T> curNode=getIndex(index);
Node<T> preNode=curNode.pre;
preNode.next=newNode;
newNode.pre=preNode;
curNode.pre=newNode;
newNode.next=curNode;
lenght++;
return;
}
//获取指定位置的节点
public <T> Node<T> getIndex(int index){
if(index<0 || index >=lenght){
throw new MyExciption("位置非法");
}
Node<T> cur= (Node<T>) head;
for(int i=0;i<index;i++){
cur=cur.next;
}
return cur; //把节点返回
}
//头删
public void removeFirst(){
if(head==null){ //空链表
return;
}
if(head.next==null){
head=null;
Tail=null;
lenght=0;
return;
}
// 1 2 3
Node<T> newcur=head.next;
newcur.pre=null;
head=newcur;
lenght--;
return;
}
//尾部删除
public void removeLast(){
if(head==null){ //如果是空链表
return;
}
if(head.next==null){ //只有一个节点的情况
head=null;
Tail=null;
lenght=0;
return;
}
// 1 2 3
Node<T> CurNode=Tail.pre;
CurNode.next=null;
Tail=CurNode;
lenght--;
return;
}
//按位置删除
public void removeByIndex(int index){
if(index<0 || index >=lenght){
throw new MyExciption("位置非法!!!");
}
if(index==0){ //这就相当于删除头节点
removeFirst();
return;
}
if(index==lenght-1){ //这就相当于删除尾节点
removeLast();
return;
}
//中间其他位置 1 2 3 4 5
Node<T> todelete=getIndex(index);
Node<T> preNode=todelete.pre;
Node<T> nextNode=todelete.next;
preNode.next=nextNode;
nextNode.pre=preNode;
lenght--;
return;
}
//按值删除节点 !!!
public void removeByValue(T val){
int index=indexOf(val);
if(index==-1){
throw new MyExciption("没有该值");
}
removeByIndex(index); //调用按位置删除的方法
}
//查找
public T get(int index){
if(index<0 ||index >=lenght){
throw new MyExciption("位置非法!");
}
return (T) getIndex(index).var;
}
//修改链表节点的值
public void set(int index,T val){
if(index<0 || index>=lenght){
throw new MyExciption("位置非法!");
}
Node<T> node=getIndex(index);
node.var=val;
}
//根据值找下标的位置
public int indexOf(T val) {
Node<T> cur=head;
for(int i=0;i<lenght;i++){
if (cur.var==val){
return i;
}
cur=cur.next;
}
return -1;
}
public static void main(String[] args) {
//创建一个实例对象
MyLinkedList<Integer> list=new MyLinkedList<>();
System.out.println("初始情况下链表的长度:"+list.lenght);
//测试添加元素
list.addHead(1);
list.addLast(6);
list.add(3,2);
// list.removeFirst();
// list.removeLast();
// list.removeFirst();
list.removeByIndex(2);
System.out.println(list.getIndex(1).var);
System.out.println(list.getIndex(0).var);
System.out.println(list.lenght);
System.out.println(list.get(1));
System.out.println(list.getIndex(1));
list.set(1,8888);//修改元素的值
System.out.println(list.getIndex(1));
list.removeByIndex(1);
System.out.println(list.getIndex(0).var);
System.out.println(list.lenght);
System.out.println("=============");
MyLinkedList<String> list1=new MyLinkedList<>();
list1.add("C++",0);
list1.addHead("java");
list1.addHead("python");
for(int i=0;i<list1.lenght;i++){
System.out.println(list1.get(i));
}
}
}
测试运行:
4.顺序表VS链表
4.1顺序表
空间连续支持随机访问;但是从中间或者头部插入元素,时间复杂度为O(N),而且扩容的开销很大。
4.2链表
空间不一定是连续的,没有扩容问题,头删,尾删的时间复杂度为O(1),但是中间插入的时间复杂度也是O(N)。