本专栏文章主要用于帮助Java使用者快速上手数据结构,刷算法题!
前言
自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰。
但是这谈何容易,因为每一重天都有神兽把守,想要冲破每一重天都必须收服守护的神兽才行。
守护九重天的神兽分别是:数组、字符串、栈、队列、链表、树、散列表、堆、图。可见他们的战斗力也是逐层增强的。想只凭靠自身的能力拿下他们谈何容易。
不过大家不必惊慌,我这里有一本上古秘籍《Java小子怒闯数据结构九重天》,里面有每一重天神兽的攻略。只要修炼者仔细钻研里面的每一篇,对九重天了如指掌之后,冲破这九重天也是易如反掌的。
今天为大家带来第五重天的攻略!
1.🌀链表的基础知识
链表是一种线性表数据结构。它使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据。
简单来说链表是实现线性表的链式存储结构的基础。
链表是一种通过指针串联在一起的线性结构,每一个节点是由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
节点情况如下:
上面的介绍都是单链表相关的,除了单链表我们还有双向链表,循环链表等。
双链表
双链表顾名思义就是比单链表高级了一点,一个双链表的结点在单链表的基础上又增加了一个指针,这个指针指向它的前一个结点
节点情况如下:
循环链表
之前的链表都是条状的,循环链表是头尾相连了,就是将链表的尾部与头部连接起来。有因为条状链表有单链表与双链表之分,所以循环链表也有两种分别是:循环单链表与循环双链表
对于循环单链表我们一般设置尾指针这样操作效率会更高。
对于循环双链表我们头结点的prior结点指向最后一个节点,尾结点的next指针指向头结点,以形成循环双链表。
2.🌀Java实例化链表
想要实例化链表就得先创建一个链表的结构类,这个类代表的是每一个节点结构。
之前提到过每一个节点是由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
所以链表节点结构定义为:
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
使用定义的这个链表结构我们可以自己构造一个链表如下:
ListNode n1 = new ListNode(4);
ListNode n2 = new ListNode(5);
ListNode n3 = new ListNode(1);
n1.next = n2;
n2.next = n3;
n3.next = null;
我们构造出来的链表如下图:
3.🌀Java实现单链表
其中实现了Iterable 接口,提供了遍历方式。
import java.util.Iterator;
public class LinkList<T> implements Iterable<T>{
private Node head;
private int N;
public LinkList() {
this.head = new Node(null,null);
N = 0;
}
private class Node{
T item;
Node next;
public Node(T item,Node next) {
this.item = item;
this.next = next;
}
}
public void clear(){
head.item=null;
head.next=null;
this.N=0;
}
public boolean isEmpty(){
return this.N==0;
}
public int length(){
return this.N;
}
public T get(int i){
if(i<0 || i > this.N){
throw new RuntimeException("位置不合法");
}
Node n = head;
for(int index=0; index<i; index++){
n = n.next;
}
return n.item;
}
public void insert(T t){
Node n = head;
while(true){
if(n.next == null){
break;
}
n = n.next;
}
Node node = new Node(t, null);
n.next =node;
this.N ++;
}
public void insert(T t,int i){
if(i < 0 || i > this.N){
throw new RuntimeException("插入位置非法");
}
Node pre = head;
for(int index=0;index <= i-1; index++){
pre = pre.next;
}
Node current = pre.next;
Node newNode = new Node(t,null);
pre.next = newNode;
newNode.next = current;
this.N++;
}
public T remove(int i){
if(i < 0 || i >this.N){
throw new RuntimeException("删除位置有误");
}
Node n =head;
for(int index=0;index <= i-1;index ++){
n = n.next;
}
Node curr = n.next;
n.next = curr.next;
this.N--;
return curr.item;
}
public int indexof(T t){
Node n = head;
for(int i = 0; n.next != null;i++){
n =n.next;
if(n.item.equals(t)){
return i;
}
}
return -1;
}
@Override
public Iterator iterator() {
return new Iterator() {
Node n =head;
@Override
public boolean hasNext() {
return n.next !=null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
};
}
}
补充一点:链表的赋值给新的链表后,两个链表是会相会影响的,说白了就是把地址赋值给它了,他们操作是同一块内存的同一个对象。
Node n = head;
把head赋值给n,现在对n操作也是会影响head的。
4.🌀链表进阶练习
Leetcode 206. 反转链表 题解:
我们可以申请两个指针,第一个指针叫 pre,最初是指向 null 的。 第二个指针 cur 指向 head,然后不断遍历 cur。 每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。 都迭代完了(cur 变成 null 了),pre 就是最后一个节点了。
代码如下:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
结语
恭喜你修炼到这里,你已经基本有了收服神兽链表的能力。神兽链表是我们到进攻算法界最重要的能力之一。大家不可懈怠。
感兴趣的修炼者可以关注下面公众号,会持续推送最新更新的!
持续更新中…
|