线性表的链式存储结构:
为什么采用链式存储结构这种数据结构?
–》因为顺序存储结构插入或删除元素时候会涉及大量元素移动,非常影响效率。
因此引入了链式存储结构为了弥补顺序存储结构效率上的问题。
链式存储结构的定义:
1.我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。
2.指针域中存储的信息称为指针或链。这两部分信息组成数据元素称为存储映像,称为结点(Node)。
3.n个结点链接成一个链表,即为线性表(a1, a2, a3, …, an)的链式存储结构。
4.因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
#一个节点Node如下图所示:
一个单链表的结构如下图所示:
链表的基本概念:
我们把链表中的第一个结点的存储位置叫做头指针,把链表中的第一个节点称为头结点。
头结点分为虚拟头结点和真是头结点:
虚拟头结点
:指第一个节点不存储数据,只是用来指向下一个节点的地址。如下图红色节点所示:
没什么用,但实际工程中,发现增加头结点代码变简单了、维护性变好了,所以其仅仅辅助我们简化代码,其不包含任何信息,仅仅包含一个指向直接后继的地址;指向的是线性表中的第零个元素;
真实头结点
:第一个节点也是用来存储数据。如下图所示:
数据节点
:链表中代表数据元素的节点,表现形式为:(数据元素,地址);
尾节点:
链表中的最后一个数据节点,包含的地址信息为空;
头指针与头结点的异同
头指针
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
无论链表是否为空,头指针均不为空。
头指针是链表的必要元素。
头结点
头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
头结点不一定是链表的必须要素。
链式结构的代码实现:
讲完了基本概念,我们来了解一下链式存储结构具体的方法是如何实现的(本文采用虚拟头结点):
链表中插入元素分为两种方式:头插法和尾插法
。
头插法
:头插法是新增节点总是插在头部,以带头结点链表为例,链表头指针是Head,新增节点p
那么
p->next = Head->next;
Head->next = p;
如果是不带头结点的链表那么对应是
p->next = Head;
Head = p;
尾插法
:而尾插法是将新增节点插在链表尾部,
for(t = Head; t->next; t=t->next); //结束时t指向尾节点
p->next = NULL; //进行插入
t->next = p;
具体代码实现:
@Override
public void add(int index, E e) {
if(index<0||index>size){
throw new IllegalArgumentException("插入角标非法!");
}
Node n=new Node(e,null);
if(index==0){ //头插
n.next=head.next;
head.next=n;
if(size==0){
rear=n;
}
}else if(index==size){ //尾插
rear.next=n;
rear=rear.next;
}else{ //一般插入
Node p=head;
for(int i=0;i<index;i++){
p=p.next;
}
n.next=p.next;
p.next=n;
}
size++;
}
链表中删除元素的方法:
删除元素示意图如下图所示:
链表第i个数据删除结点的算法思路:
声明结点p指向链表第一个结点,初始化j=1;
当j<1时,就遍历链表,让P的指针向后移动,不断指向下一个结点,j累加1;
若到链表末尾p为空,则说明第i个元素不存在;
否则查找成功,将欲删除结点p->next赋值给q;
单链表的删除标准语句p->next = q->next;
将q结点中的数据赋值给e,作为返回;
释放q结点。
代码实现如下:
public E remove(int index) {
if(index<0||index>=size){
throw new IllegalArgumentException("删除角标非法!");
}
E res=null;
if(index==0){ //头删
Node p=head.next;
res=p.data;
head.next=p.next;
p.next=null;
p=null;
if(size==1){
rear=head;
}
}else if(index==size-1){//尾删
Node p=head;
res=rear.data;
while(p.next!=rear){
p=p.next;
}
p.next=null;
rear=p;
}else{ //一般删除
Node p=head;
for(int i=0;i<index;i++){
p=p.next;
}
Node del=p.next;
res=del.data;
p.next=del.next;
del.next=null;
del=null;
}
size--;
return res;
}
以上就是链式存储结构的基本概念,下面附全代码供参考:
package com.lfz.链表;
import com.lfz.线性表.*;
public class LinkedList<E> implements List<E> {
/**
* 单向链表的结点类
* */
private class Node{
E data; //数据域
Node next; //指针域
public Node(){
this(null,null);
}
public Node(E data,Node next){
this.data=data;
this.next=next;
}
@Override
public String toString() {
return data.toString();
}
}
private Node head; //指向虚拟头结点的头指针
private Node rear; //指向尾结点的尾指针
private int size; //记录元素的个数
public LinkedList(){
head=new Node();
rear=head;
size=0;
}
public LinkedList(E[] arr){
this();
for (E e : arr) {
addLast(e);
}
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0&&head.next==null;
}
@Override
public void add(int index, E e) {
if(index<0||index>size){
throw new IllegalArgumentException("插入角标非法!");
}
Node n=new Node(e,null);
if(index==0){ //头插
n.next=head.next;
head.next=n;
if(size==0){
rear=n;
}
}else if(index==size){ //尾插
rear.next=n;
rear=rear.next;
}else{
Node p=head;
for(int i=0;i<index;i++){
p=p.next;
}
n.next=p.next;
p.next=n;
}
size++;
}
@Override
public void addFirst(E e) {
add(0,e);
}
@Override
public void addLast(E e) {
add(size,e);
}
@Override
public E get(int index) {
if(index<0||index>=size){
throw new IllegalArgumentException("查找角标非法!");
}
if(index==0){
return head.next.data;
}else if(index==size-1){
return rear.data;
}else{
Node p=head;
for(int i=0;i<=index;i++){
p=p.next;
}
return p.data;
}
}
@Override
public E getFirst() {
return get(0);
}
@Override
public E getLast() {
return get(size-1);
}
@Override
public void set(int index, E e) {
if(index<0||index>=size){
throw new IllegalArgumentException("修改角标非法!");
}
if(index==0){
head.next.data=e;
}else if(index==size-1){
rear.data=e;
}else{
Node p=head;
for(int i=0;i<=index;i++){
p=p.next;
}
p.data=e;
}
}
@Override
public boolean contains(E e) {
return find(e)!=-1;
}
@Override
public int find(E e) {
int index=-1;
if(isEmpty()){
return index;
}
Node p=head;
while(p.next!=null){
p=p.next;
index++;
if(p.data==e){
return index;
}
}
return -1;
}
@Override
public E remove(int index) {
if(index<0||index>=size){
throw new IllegalArgumentException("删除角标非法!");
}
E res=null;
if(index==0){ //头删
Node p=head.next;
res=p.data;
head.next=p.next;
p.next=null;
p=null;
if(size==1){
rear=head;
}
}else if(index==size-1){//尾删
Node p=head;
res=rear.data;
while(p.next!=rear){
p=p.next;
}
p.next=null;
rear=p;
}else{
Node p=head;
for(int i=0;i<index;i++){
p=p.next;
}
Node del=p.next;
res=del.data;
p.next=del.next;
del.next=null;
del=null;
}
size--;
return res;
}
@Override
public E removeFirst() {
return remove(0);
}
@Override
public E removeLast() {
return remove(size-1);
}
@Override
public void removeElement(E e) {
int index=find(e);
if(index==-1){
throw new IllegalArgumentException("元素不存在");
}
remove(index);
}
@Override
public void clear() {
head.next=null;
rear=head;
size=0;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append("LinkedList:size="+getSize()+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append("[");
Node p=head;
while(p.next!=null){
p=p.next;
if(p==rear){
sb.append(p.data+"]");
}else{
sb.append(p.data+",");
}
}
}
return sb.toString();
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(obj == this) {
return true;
}
if(obj instanceof LinkedList) {
LinkedList<E> list = (LinkedList<E>) obj;
if(getSize() == list.getSize()) {
for(int i=1;i<getSize();i++) {
if(get(i) != list.get(i)) {
return false;
}
}
return true;
}
}
return false;
}
}