八大数据结构——链表(三)
数据结构定义(百度解释): 数据结构(data structure)是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算,设计出相应的算法,并确保经过这些运算以后所得到的新结构仍保持原来的结构类型。
(就是你打算以什么形式放数据,是铺开丢地板上,还是靠墙堆起来,大概这样的意思。)
常用的主要有八大类型:1.数组(Array)2.栈(Stack)3.链表(Linked List)4.图(Graph)5.散列表(哈希表)(Hash)6.队列(Queue)7.树(Tree)8.堆(Heap)。
链表定义(百度解释):
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。(枯燥无味,往下看。)
看上面的图,链表常见的形式有三种:单链表,循环链表,双向链表。
我们先来说一下,无论是三种中的哪一种,它们都是由节点组成的,节点就是上图中一块块的玩意。节点的组成就两部分:
1.存储数据的空间
2.存储下一个节点的指针
对于一个节点来说,存储空间和指针两部分可以看成是连续的一段地址空间,但节点与节点之间,就是不连续的一段地址空间。指针里面存的是下一个节点的地址,通过这个地址就可以找到下一个节点。
特性:
1.链表是不连续分配的一段地址空间。
2.链表是由一个个节点组成。
3.不需要在初始化的时候,指定容量。
优点:
1.添加和删除操作很快。就新增或删除一个节点的事。
缺点:
1.查询和修改操作很慢。它不像数组有索引直接定位,通常它一开始只会保存第一个节点,然后从第一个节点开始,往下挨个找。(打个比方,数组是所有人(数据)你都认识,找谁直接找他就行;链表是你就认识一个人,一个挨一个地传话,一直传到那个要找的人。)
2.需要的空间多,他保存一个数据,不仅要一个存储空间,还多要一个指针空间。
适用场景:
数据量较小,需要频繁增加,删除操作的场景
java代码实现
下面通过Java来实现一个LinkedList,这里实现一个单链表。基于jdk1.8.
首先,我们要把最重要的部分,节点写出来。节点因为要用到很多个,直接写成一个类,要一个就new一个。
class LinkedNode<Type> {
//定义节点,存储数据和对下一个节点的引用
public Type val;
public LinkedNode<Type> nextNode;
}
然后,我们开始来写链表类啦,先写一下我们要定义的属性,private类型,不能给外面的人看到!头节点,必须的。我们再多存一个尾节点,这样在末尾添加的时候,就方便很多。
private LinkedNode<Type> firstNode;//头节点
private LinkedNode<Type> lastNode;//尾节点
private int size;
构造方法,没啥特别的,我习惯多写两个。这里的size你也可以初始化为0,都无所谓,只不过后对size的判断要自己注意,不能用我的了。
public LinkedList(){
clear();
}
public LinkedList(Type val) {
clear();
insert(val);
}
public void clear() {//清空链表
firstNode=lastNode=new LinkedNode<Type>();
size=-1;
}
来个插入到末尾,要注意处理一下刚开始的情况。
public void insert(Type val) {//在链表最后插入元素
if(size==-1) {
firstNode.val=val;
size++;
return;
}else {
LinkedNode<Type> node = new LinkedNode<>();
node.val=val;
lastNode.nextNode=node;
lastNode=node;
size++;
return;
}
}
再来个查找。从头开始一个个问,慢。。。
private LinkedNode<Type> find(int index) {//通过索引查找节点
if(index<0||index>size) {
throw new ArrayIndexOutOfBoundsException();
}
LinkedNode<Type> node = firstNode;
int count = 0;
//从头节点开始,一直到count等于index
while(count<index) {
node=node.nextNode;
count++;
}
return node;
}
public Type get(int index){
return find(index).val;
}
删除操作,打个比方,现在有三个节点:爷爷->爸爸->儿子,现在要删除爸爸,那就直接让爷爷的指针,指向儿子,爷爷->儿子,这样爸爸就被踢出去了。
public Type remove(int index){//删除指定位置的元素
LinkedNode<Type> node = find(index);
if(index==0) {
firstNode=node.nextNode;
size--;
return node.val;
}
LinkedNode<Type> before = find(index-1);
before.nextNode=node.nextNode;
size--;
return node.val;
}
完整代码
//实现一个只有单向的链表
public class LinkedList<Type> {
private LinkedNode<Type> firstNode;//头节点
private LinkedNode<Type> lastNode;//尾节点
private int size;
public LinkedList(){
clear();
}
public LinkedList(Type val) {
clear();
insert(val);
}
public void clear() {//清空链表
firstNode=lastNode=new LinkedNode<Type>();
size=-1;
}
public int size() {//返回链表存储的元素个数
return size+1;
}
public void insert(Type val) {//在链表最后插入元素
if(size==-1) {
firstNode.val=val;
size++;
return;
}else {
LinkedNode<Type> node = new LinkedNode<>();
node.val=val;
lastNode.nextNode=node;
lastNode=node;
size++;
return;
}
}
private LinkedNode<Type> find(int index) {//通过索引查找节点
if(index<0||index>size) {
throw new ArrayIndexOutOfBoundsException();
}
LinkedNode<Type> node = firstNode;
int count = 0;
//从头节点开始,一直到count等于index
while(count<index) {
node=node.nextNode;
count++;
}
return node;
}
public Type get(int index){
return find(index).val;
}
public int getIndex(Type val){//返回该元素的索引,若重复,则返回第一个
LinkedNode<Type> node = firstNode;
int count = 0;
while(count<=size) {
if(node.val.equals(val)) {
return count;
}
if(node.nextNode!=null) {
node=node.nextNode;
}
count++;
}
// throw new ListItemNotFoundException();
return -1;
}
public void insert(Type val,int index) {//在指定位置插入元素
if(index<0||index>size+1) {
throw new ArrayIndexOutOfBoundsException();
}
if(index==size+1) {//若插入位置是末尾,则调用末尾的插入方法
insert(val);
return;
}
if(index==0) {//插入位置在起始
LinkedNode<Type> node = new LinkedNode<>();
node.val=val;
node.nextNode=firstNode;
firstNode=node;
size++;
return;
}else {//插入位置在中间
LinkedNode<Type> beforeNode = find(index-1);
LinkedNode<Type> node = new LinkedNode<>();
node.val=val;
node.nextNode=beforeNode.nextNode;
beforeNode.nextNode=node;
size++;
return;
}
}
public void insert(Type before,Type val) {//在指定元素后面插入元素,若指定元素重复,是在第一个后插入
insert(val,getIndex(before)+1);
}
public Type remove(int index){//删除指定位置的元素
LinkedNode<Type> node = find(index);
if(index==0) {
firstNode=node.nextNode;
size--;
return node.val;
}
LinkedNode<Type> before = find(index-1);
before.nextNode=node.nextNode;
size--;
return node.val;
}
public boolean remove_first(Type val) {//删除指定元素,若重复,则只删除第一个
try {
int index = getIndex(val);
remove(index);
}catch(ArrayIndexOutOfBoundsException e) {
return false;
}
return true;
}
public void removeAll(Type val) {//删除链表中所有该元素
while(remove_first(val));
}
public Type replace(int index,Type val) {//替换指定位置的元素
LinkedNode<Type> node = find(index);
Type old = node.val;
node.val=val;
return old;
}
public boolean replace(Type old,Type val) {//替换指定元素,若有重复,则替换第一个
try {
int index = getIndex(old);
LinkedNode<Type> node = find(index);
node.val=val;
}catch(ArrayIndexOutOfBoundsException e) {
return false;
}
return true;
}
public void replaceAll(Type old,Type val) {//替换链表中所有该元素
while (replace(old,val));
}
public LinkedList<Type> find_All(Type val){//查询链表中所有该元素,并以链表形式返回
LinkedList<Type> link = new LinkedList<>();
LinkedNode<Type> node = firstNode;
while(node!=null) {
if(node.val.equals(val)) {
link.insert(val);
}
node=node.nextNode;
}
return link;
}
}
class LinkedNode<Type> {
//定义节点,存储数据和对下一个节点的引用
public Type val;
public LinkedNode<Type> nextNode;
}