您的位置:首页 > 理论基础 > 数据结构算法

单链表结构

2016-08-24 21:38 260 查看
链表是一种复杂的数据结构,其数据之间的相互关系使链表分成三种:单链表、循环链表、双向链表,下面将逐一介绍。

单链表:单链表有一个头节点head,指向链表在内存的首地址。链表中的每一个节点的数据类型为结构体类型(数据的结构类型,有一个数据域和指针域共同组成了这种新的数据),节点有两个成员:整型成员(实际需要保存的数据,可以是泛型)和指向下一个结构体类型节点的指针即下一个节点的地址。链表按此结构对各节点的访问需从链表的头找起,后续节点的地址由当前节点给出。无论在表中访问那一个节点,都需要从链表的头开始,顺序向后查找。链表的尾节点由于无后续节点,其指针域为空,写作为NULL。

单链表的创建过程:

1、定义一个单链表的数据结构,可以说是一种新的数据类型

2、创建一个空的链表

3、分配一个指针域为NULL,数据域不设置的头结点

4、可以进行链表的使用,加入元素,将新节点的指针域成员赋为NULL,通过循环判断指针域的值是否为NULL,来找到最后一个节点或者是第一个节点

单链表的使用过程:

1、把单链表的头结点传入

2、通过检测节点中的指针域是否非NULL,来循环数据节点中的数据域

3、找到下一个节点的地址,返回第二步

C语言单链表的形式

#include <stdio.h>
#include <stdlib.h>
typedef struct LNode LNode;//新数据类型
typedef struct LNode* pLNode;//指针数据类型
//1、定义结构
struct LNode{
int data; //数据域
struct LNode *next; //指针域
};
pLNode addLNode(pLNode p,int i);
pLNode init(pLNode p);
void show(pLNode p);

int main()
{
pLNode p=NULL; //2、创建空表
//p=(pLNode)malloc(sizeof(LNode));
p =init(p);
int i=0;
for(i;i<100;i++)
{
p = addLNode(p, i);
}
show(p);
}
pLNode init(pLNode p)
{
p=(pLNode)malloc(sizeof(LNode));
p->next=NULL;
return p;
}
pLNode addLNode(pLNode p,int i)
{
pLNode head = p;
pLNode p1=(pLNode)malloc(sizeof(LNode)); //3、使用malloc函数进行动态内存分配
p1->next=NULL;                      //4、把新节点的指针域赋值为null
while(p->next!=NULL){   //并找到最后一个节点或者是第一个节点
p=p->next;
}
p->next=p1;    //5、给最后一个指针域赋值为新创建的节点
p1->data=i;
//给最后一个节点赋值
return head;
}

void show(pLNode p)
{
while(p->next!=NULL)
{
p=p->next;
printf(" %d  ",p);
printf("  %d\n",p->data);

}
}


网上找的一幅图



Java语言编写的数据结构

1、第一种实现方式,只有一个头结点和一个节点个数属性,节点内容一样,就是链表的定义方式实现

public class MySigleLinkListe<E> {
public static class Node<E>{
Node<E> pNext;
E item;
public Node(Node<E> pNext, E item) {
super();
this.pNext = pNext;
this.item = item;
}
@Override
public String toString() {
return "Node [item=" + item + "]";
}

}

public MySigleLinkListe() {
super();
head=new Node<E>(null, null);  //穿件头指针,java中存储方式不一样,
}

transient Node<E> head; //头结点
transient int size; //节点数量

public void add(E e){
addLast(e);
}

private void addLast(E e){
final Node<E> n=new Node<E>(null,e);  //新 创建了 一个节点
Node<E> m=head;   //先把节点引用赋给一个中间变量引用
if(m.pNext==null) //若果这个节点中的pNext为null,说明是头结点
m.pNext=n;   //则把新节点引用赋给投头结点中的pNext属性
else{
while(m.pNext!=null){ //如果已存在节点,则开始遍历,利用pNext属性为null这一特性来查找最后一个节点,
m=m.pNext;     //成立,则把当前引用改为 下一个引用,继续遍历
}
m.pNext=n; //直到最后一个节点,这时把新节点引用赋给最后一个节点的pNext属性
}
size++;//size++
}

public void show(){
if(head.pNext==null){
return ;
}
Node<E> m=head;
while(m.pNext!=null){
m=m.pNext;
System.out.println(m.item.toString());
}
}
//存储 个数
public int count(){
return size;
}
}




使用部分代码

MySigleLinkListe<String> my=new MySigleLinkListe<String>();
for (int i = 0; i < 10; i++) {
my.add(i+"");
}
my.show();
System.out.println(my.count());


2、第二种实现方式,使用两个引用,一个头引用,一个尾引用,这样就利于添加

public class MyDoubleLinkList<E> {
private static class Node<E>{
E e;
//Node<E> pFirst;  //头引用
Node<E> pNext;   //下一个节点的引用
public Node(E e, Node<E> pNext) {
super();
this.e = e;
this.pNext = pNext;
}

}
transient Node<E> head; //头结点
transient Node<E> end;  //尾节点
transient int size;    //节点数
public MyDoubleLinkList() {
super();
head=new Node<E>(null, null);
end=head;
}
public void add(E e){
addLinkLast(e);
}
private void addLinkLast(E e){
Node<E> n=new Node<E>(e, null);
Node<E> m=head;
if(end==head){    //头节点等于尾节点时则说明还没有子节点
m.pNext=n;   //把头结点的pNext指向下一个节点
end=n;      //尾节点指向新节点
}else{
end.pNext=n;  //把前一次的尾节点保存为新节点
end=n;     //把新节点保存为尾节点 ,这样可以不用每次插入都找尾节点,效率提升了很多,
}
size++;
}
public void show(){
Node<E> m=head;
while(m.pNext!=null){
m=m.pNext;
System.out.println(m.e);
}
}
public E get(int index){
Node<E> m=head;
for (int i = 0; i < index+1; i++) {
m=m.pNext;
}
return m.e;
}
public int count(){
return size;
}
}


使用部分代码

MyDoubleLinkList<String> d=new MyDoubleLinkList<String>();
for(int j=20;j<30;j++){
d.add(j+"");
}
for(int i=0;i<d.count();i++){
System.out.print(d.get(i)+"  ");
}




感悟:源码中的linkList实现是基于双端链表的,也就是一个节点既有上一个节点引用也有下一个节点引用,这个思想都是一样的,源码中写的有些很复杂,源码中使用了一个 size属性,就是用来作为一个查找索引使用的,这样有利于查询

这是get(int index)方法的源码一部分
Node<E> node(int index) {
// assert isElementIndex(index);

if (index < (size >> 1)){  //这里直接就进行了 一半处理,这样查找的时间大多会缩短一半,
Node<E> x = first;     //如果在前一半就从头引用开始查找
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {                  //后一半就从尾引用忘前找,这个实现的前提就是使用双端链表
Node<E> x = last;    //,才能找到上一个引用
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构