数据结构——第一章线性表:03线性表的链式存储结构

1.单链表:用一组地址任意的存储单元存放线性表中的数据元素。以元素(数据元素的映象)+ 指针(指示后继元素存储位置)= 结点(表示数据元素)。

2.以线性表中第一个数据元素a1的存储地址作为线性表的地址,称作线性表的头指针。为了操作方便,在第一个结点之前加一个辅助结点“头结点”,以指向头结点的指针作为链表的头指针。

3.单链表的C语言描述:

typedef struct LNode

{

  ElemType data; //数据域

  struct LNode* next; //指针域

} LNode, *LinkList;

4.单链表操作的实现:

(1)getElem(L, i, &elem):单链表是一种顺序存取的结构,为找第i个数据元素,必须先找到第i-1个数据元素。实现如下:

void getElem(LinkList L, int i, ElemType &elem) //L是带头结点链表的头指针,elem返回第i个元素

{

  p = L->next; //p指向第一个结点

  count = 1; //count为计数器

  while (p && count < i) //顺链表向后查找,知道p指向第i个元素或p为空

  {

    p = p->next; 

    count++;

  }

  if (!p || j > i)

  {

    return; //第i个元素不存在

  }

  elem = p->data; //取得第i个元素

}

(2)listInsert(&L, i, elem):有序对<ai-1, ai>变为<ai-1, e>和<e, ai>。在链表中插入结点只需要修改指针,若要在第i个结点之前插入元素,修改的是第i-1个结点的指针。因此,在单链表中第i个结点之前进行插入的基本操作为:找到线性表中第i-1个结点,然后修改其指向后继的指针。实现如下:

void  listinsert(LinkList &L, int i, ElemType elem) //L为带头结点的单链表的头指针,本算法在链表中第i个结点之前插入新的元素e

{ 

  p = L;

  j = 0;

  while (p && j < i - 1)

  {

    p = p->next;

    count++; //寻找第i-1个结点

  }

  if (!p || j > i - 1)

  {

    return;

  }

  s = new LNode; //生成新结点

  if (s == NULL)

  {

    return;

  }

  s->data = elem;

  s->next = p->next;

  p->next = s; //插入新结点

} //算法的时间复杂度为:O(ListLength(L))

(前插结点:设p指向链表中某结点,s指向待插入的值为x的新结点,将*s插入到*p的前面,与后插不同的是:首先要找到*p的前驱*q,然后再完成在*q之后插入*s,设单链表头指针为L。操作如下:

①q = L;

while (q->next != p)

{

  q = q->next; //找*p的直接前驱

}

②s->next = q->next;

③q->next = s;

前插操作因为要找*p的前驱,时间性能为O(n)。前插操作可以用后插操作来实现,即仍然可以将*s插入到*p的后面,然后将p->data与s->data交换即可。这样既满足了逻辑关系,也能使得时间复杂度为O(1)。)

(3)listDelete(&L, i):有序对<ai-1, ai>和<ai, ai+1>,变为<ai-1, ai+1>。在单链表中删除第i个结点的基本操作为:找到线性表中第i-1个结点,修改其指向后继的指针。实现如下:

void listDelete(LinkList &L, int i) //删除以L为头指针(带头结点)的单链表中第i个结点

{

  p = L;

  j = 0;

  while (p->next && j < i - 1) //寻找第i个结点,并令p指向其前驱

  {

    p = p->next;

    j++;

  }

  if (!(p->next) || j > i - 1)

  {

    return; //删除位置不合理

  }

  q = p->next;

  p->next = q->next; //删除并释放结点

  delete(q);

} //算法的时间复杂度为:O(ListLength(L))

(4)clearList(&L)的实现如下:

void clearList(LinkList &L) //将单链表重新置为一个空表

{

  while (L->next)

  {

    p = L->next;

    L->next = p->next;

    delete(p);

  }

} //算法的时间复杂度为:O(ListLength(L))

5.从线性表得到单链表:链表是一个动态的结构,它不需要预分配空间,因此生成链表的过程是一个结点逐个插入的过程。

(1)头插法:逆位序插入n个数据元素的值,建立带头结点的单链表。操作步骤:

①建立一个空表

②输入数据元素an,建立结点并插入

③输入数据元素an-1,建立结点并插入

LinkList createList(int n) //头插法建立带头结点的单链表

{

  L = new LNode; //先建立一个头结点

  L->next = NULL;

  q = L;

  for (i = n; i > 0; i--)

  {

    p = new LNode;

    scanf(&p->data); //输入元素值

    p->next = q->next;

    q->next = p;

  }

  return L;

} //算法的时间复杂度为:O(ListLength(L))

(2)尾插法:顺位序插入n个数据元素的值,建立带头结点的单链表。

LinkList createList() //尾插法建立带头结点的单链表

{

  LinkList L = (LNode*)malloc(sizeof(LNode));

  L->next = NULL;

  LNode *s, *r = L; //空表

  int x;

  scanf("%d", &x);

  while (x != flag)

  {

    s = (LNode*)malloc(sizeof(LNode));

    s->data = x;

    r->next = s;

    r = s; //r指向新的尾结点

    scanf("%d", &x);

  }

  r->next = NULL; //尾结点指针域需置为空

  return L;

}

6.建立头结点的两个优点:

(1)由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作就和在表的其它位置上的操作一致,无需进行特殊处理;

(2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一了。

posted @ 2018-10-29 11:35  H36Phaeton  阅读(408)  评论(0编辑  收藏  举报