浅析数据结构之线性表<二>

线性表之链式存储与实现

 与顺序表不同的是,单链表是一种链式存取的数据结构,存储单元地址随意,不连续。
链表中的数据以结点表示,结点由数据域和指针域构成,描述如下。

typedef int ElemType;
typedef struct link
{
    ElemType data;
    struct link *next;
} Lnote,*LinkList;
说到单链表,就不得不谈一下指针了,Lnode L;定义中,L是一个单链表数据类型的变量,而LinkList P;P是一个指向单链表数据类型的指针变量。
何谓数据类型?
数据类型的意义在于所分得的存储单元的大小,当计算机要取用一个整型的变量时,可以根据其所分配到的起始地址及其数据类型的所匹配的存储单元的大小取出一个正确的数据,结构体也一样,结构体的定义在于自定义数据类型,分配一个匹配结构体所需存储单元大小的空间用于存储结构体数据,*P=L;程序中定义的每个变量都存在某个地址,在那个地址中存着变量的值,而P变量的值是L的地址,故在变量P的地址处存的数据是L地址,用malloc函数开辟空间,相当于是为p赋初值,流程为,找到一块L数据类型大小的空闲
可分配 空间,然后将这块空间的初始地址的值赋予P
既free(p)就是要释放指针p所指向的内存,使其重新变为可分配。
下面我们开始创建单链表。
先创建一个空表,并创建为一个具有n个结点的单链表
//构造一个空的单链表
LinkList InitList(LinkList L)
{
    L=(LinkList)malloc(sizeof(Lnote));
    L->next=NULL;
    return L;
}

//尾插法创建一个具有n个结点的单链表,返回值为链表指针,
LinkList CreateList_tail(ElemType *e,int n)
{
    int i;
    LinkList head,p1,p2;
    head = InitList(head);
    p1 = head;
    for(i=0; i<n; i++)
    {
        p2=(LinkList)malloc(sizeof(Lnote));
        p2->data = e[i];
        p2->next=p1->next;
        p1->next = p2;
        p1= p2;
    }
    return head;
}

//头插法创建一个具有n个结点的单链表,返回值为链表指针,
LinkList CreateList_top(ElemType *e,int n)
{
    int i;
    LinkList head,p;
    head = InitList(head);
    for(i=n-1; i>=0; i--)
    {
        p=(LinkList)malloc(sizeof(Lnote));
        p->data = e[i];
        p->next = head->next;
        head->next = p;
    }
    return head;
}

/*举例,指针的指针运用*/
//运用指针的指针法创建一个单链表
void CreateList_top_1(ElemType *e,int n,LinkList *L)
{
    (*L)=CreateList_top(e,n);//等价于p=CreateList_tail(e,n);
}
指针的指针指向指针,再来看单链表的逆置
//单链表的逆置
//方法1
void Reverse_2(LinkList *L)
{
    LinkList L1,L2;
    L1=*L;
    L1=(*L)->next;
    (*L)=NULL;
    while(L1)
    {
        L2=L1;
        L1=L1->next;
        L2->next=(*L);
        (*L)=L2;
    }
    L1=(LinkList)malloc(sizeof(Lnote));
    L1->next=(*L);
    (*L)=L1;
}
//单链表的逆置
//方法2
void Reverse_1(LinkList *L)
{
    LinkList *p,*q;
    p = (LinkList*)malloc(sizeof(LinkList));  //存储LinkList*变量,p存储的是LinkList地址,*p存储的是Lnote地址
    q = (LinkList*)malloc(sizeof(LinkList));  
    (*p)=(*L)->next;
    (*L)=NULL;
    while(*p)
    {
        (*q)=(*p);
        (*p)=(*p)->next;
        (*q)->next=(*L);
        (*L)=(*q);
    }
    (*p)=(LinkList)malloc(sizeof(Lnote));
    (*p)->next=(*L);
    (*L)=(*p);
}
方法二与方法一有什么不同呢,为什么要
 p = (LinkList*)malloc(sizeof(LinkList));  
 q = (LinkList*)malloc(sizeof(LinkList));
如图

P指向中间那个地址,P中存着L的地址
L指向表结点,L存着表结点的地址,
(*p)=(*S)->next;获取到的值(表结点的地址)存于中间那一块。如不分配空间,则相当于得到了一个值,却无处可存。
中间那块和第三个的指针域中存储的都是表结点的地址。它们都是指向表结点数据类型的指针

而指针,
在32位机上,所有指针类型变量占用内存字节数都为4
因为32位机就是 4字节 * 8个二进制位/字节 计算出来的.
指针占用内存的大小其实都是一样的,不一样的只是指向的数据类型不同

因此指针分指向类型的主要作用就是,假如指针是指向int型,它除了给出int这个数据的地址,同时规定计算机每次读取数据都读取4字节为一个数据(按int型数据所占内存的大小)

下面继续剩下的代码
//销毁单链表
void destor(LinkList L)
{
    LinkList p = L;
    while(p)   
    {
        L = L->next; 
	free(p);     
        p=L;
    }
}

//单链表置为空表
void clearList(LinkList L)
{
    L->next = NULL;    //头结点不指向任何表结点
}

//若单链表为空表返回0,否则返回1
int ListEmpty(LinkList L)
{
    if(L->next)
        return 0;
    return 1;
}

//返回前趋元素的地址
LinkList PriorAdress(LinkList L,ElemType e)
{
    while(L->next)        //如果下一个为空,则无需继续判断,本元素已是最后一个,不是前趋
    {
        if(L->next->data==e)
            return L;
        L=L->next;
    }
    return NULL;
}

//返回前趋元素数据
ElemType PriorElem(LinkList L,ElemType e)
{
    LinkList p = PriorAdress(L,e);   //得到前趋元素的地址
    if(p)                            //如地址不为空,此元素存在
        return p->data;
}

//返回后继元素的地址
LinkList NextAdress(LinkList L,ElemType e)
{
    LinkList p;
    p = PriorAdress(L,e)->next->next;    //通过前趋元素的地址,得到后继元素的地址
    return p;
}

//返回后继元素数据
ElemType NextElem(LinkList L,ElemType e)
{
    LinkList p = NextAdress(L,e);         //得到后继元素的地址
    if(p)                            //如地址不为空,此元素存在
        return p->data;
}

//删除单链表中第1个与e相同的元素
void ListDelete_2(LinkList L,ElemType e)
{
    LinkList p1,p2;
    p1=PriorAdress(L,e);              //得到第1个与e相同的元素的前趋元素的地址,如其有前趋元素,则其必不为空,无需判断if(p1)
    p2= p1->next;
    p1->next=p2->next;
    free(p2);
}

//返回第i个元素的地址
LinkList IAdress(LinkList L,int i)
{
    int n=0;
    if(i==0)return L;
    while(L->next)
    {
        n++;
        L=L->next;
        if(i==n)
            return L;
    }
}

//返回第i个元素数据
ElemType IElem(LinkList L,int i)
{
    LinkList p = IAdress(L,i);            //得到第i个元素的地址
    if(p)                                  //如地址不为空,此元素存在
        return p->data;
    return -1;
}

//第i个位置插入元素e
void ListInsert(LinkList L,int i,ElemType e)
{
    LinkList p1,p2;
    p1=IAdress(L,i-1);               //得到第i-1个元素的地址
    if(p1)                 //如第i-1个元素的地址为空,则无法在第i个位置插入元素
    {
        p2=(LinkList)malloc(sizeof(Lnote));
        p2->data=e;
        p2->next=p1->next;
        p1->next=p2;
    }
}

//删除单链表中第i个元素
void ListDelete_1(LinkList L,int i)
{
    LinkList p1,p2;
    p1=IAdress(L,i-1);             //得到第i-1个元素的地址
    if(p1->next)                         //如第i个元素的地址不为空,此元素存在
    {
        p2= p1->next;
        p1->next=p2->next;
        free(p2);
    }
}

//返回单链表元素个数
int ListLength(LinkList L)
{
    int i = 0;
    while(L->next)
    {
        i++;
        L=L->next;
    }
    return i;
}
//单链表排序
LinkList sort1(LinkList head)
{
 LinkList p3=head;
 LinkList p2,p1,p4,p5;
 while(p3->next->next!=NULL)//注意链表向前推进的方式
 {
  p2=p3->next;
  p5=p2;
  p1=p2;
  int flag=0;

  while(p1->next!=NULL)//内存循环终止的条件
  {
   if(p5->data>p1->next->data)//找出原链表中剩余节点中值最小的那个节点
   {
    p5=p1->next;
    p4=p1;
    flag=1;
   }
   p1=p1->next;
  }

  if(flag)//交换两个节点的顺序,值小的节点往前调
  {
   if(p2==p4)//此种情况为要交换顺序的两个节点相邻
   {
    p2->next=p5->next;
    p5->next=p2;
    p3->next=p5;
   }
   else//这种情况为要交换的两个节点不相邻
   {
    LinkList temp=p5->next;
    p5->next=p2->next;
    p3->next=p5;
    p4->next=p2;
    p2->next=temp;
   }
  }
  p3=p3->next;
 }

 return head;
}

//输出单链表
void InputList(LinkList L)
{
    while(L->next)
    {
        L = L->next;
        printf("%d ",L->data);
    }
}
代码就这么多,main函数测试就请看客自己去写了,博主写了这么多代码,已受内伤。

不足之处欢迎指正。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值