数据结构学习笔记 --- 线性表 (单链表)

来源:互联网 发布:淘宝售后规则 编辑:程序博客网 时间:2024/05/18 13:47

1. 引言


单链表有带有结点和不带头结点之分,本文分别讨论带头结点的单链表和不带头结点的单链表的一些基本操作,和用头插法、

插法创建单链表,以及两个算法。


2. 带头结点的单链表


2.1 带头结点的单链表的存储结构
typedef struct LNode{ElemType data;struct LNode        *next;}LNode, *LinkList;

2.2 带头结点的单链表的基本操作


本文不做一一介绍,只介绍几个比较重要的基本操作。


(1)返回L中第1个与e满足关系compare()的数据元素的位序

// 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)// 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。//           若这样的数据元素不存在,则返回值为0int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)){LinkList p = L->next;inti = 0;while(p)   {     i++;     if(compare(p->data,e)) // 找到这样的数据元素       return i;     p=p->next;   }   return 0;}

(2)前驱和后继

// 初始条件: 线性表L已存在// 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,//           返回OK;否则操作失败,pre_e无定义,返回INFEASIBLEStatus PriorElem(LinkList L, ElemType cur_e, ElemType &pre_e){LinkList q,p=L->next; // p指向第一个结点   while(p->next) // p所指结点有后继   {    q=p->next; // q为p的后继     if(q->data==cur_e)     {       pre_e=p->data;       return OK;     }     p=q; // p向后移   }      return INFEASIBLE;}// 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,//           返回OK;否则操作失败,next_e无定义,返回INFEASIBLEStatus NextElem(LinkList L, ElemType cur_e, ElemType &next_e){LinkList p = L->next;  // p指向第一个结点while (p->next)  // p所指结点有后继{if ( p->data == cur_e){next_e = p->next->data;return OK;}p = p->next;}return INFEASIBLE;}
(3)插入和删除

// 在带头结点的单链线性表L中第i个位置之前插入元素eStatus InsertList(LinkList L, int i, ElemType e){intj = 0;LinkListp = L, pnew;// 寻找第i-1个结点while (j < i-1 && p){p = p->next;j++;}if (!p || j > i-1) // i小于1或者大于表长return ERROR;pnew = (LinkList)malloc(sizeof(LNode));if (!pnew)exit(OVERFLOW);pnew->data = e;pnew->next = p->next;p->next = pnew;return OK;}// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值Status DeleteList(LinkList L, int i, ElemType &e){intj = 0;LinkListp = L, q;// 寻找第i个结点,并令p指向其前驱while (j < i-1 && p->next){p = p->next;j++;}if (!(p->next) && j>i-1 ) // 删除位置不合理return ERROR;q = p->next;e = q->data;p->next = q->next;free(q);return OK;}

(4)头插法和尾插法创建单链表

// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表Lvoid HeadCreList(LinkList &L, int n){LinkList head;L = (LinkList)malloc(sizeof(LNode));if (!L) exit(OVERFLOW);L->next = NULL;printf("please input %d element.\n", n);for (int i = 0; i < n; i++){head = (LinkList)malloc(sizeof(LNode));if (!head) exit(OVERFLOW);scanf("%d", &(head->data));head->next = L->next; // 插入到表头L->next = head;}}// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表Lvoid TailCreList(LinkList &L, int n){LinkList temp, tail;L = (LinkList)malloc(sizeof(LNode));if (!L) exit(OVERFLOW);L->next = NULL;tail = L;   // 总是让tail指向链表的尾结点printf("please input %d element.\n", n);for (int i = 0; i < n; i++){temp = (LinkList)malloc(sizeof(LNode));if (!temp) exit(OVERFLOW);scanf("%d", &(temp->data));tail->next = temp;tail = tail->next;  // 总是让tail指向链表的尾结点}tail->next = NULL;// 不要忘记将尾结点的next指针置为空}

(5)两个算法

// 将所有在线性表Lb中但不在La中的数据元素插入到La中void Union(LinkList &La, LinkList Lb){int la_len = ListLength(La);LinkList pa = La->next, pb = Lb->next;while (pb){if (!LocateElem(La, pb->data, compare))InsertList(La,++la_len, pb->data);pb = pb->next;}}// 已知单链线性表La和Lb的元素按值非递减排列。// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列void MergeList(LinkList La, LinkList Lb, LinkList &Lc){LinkList pa = La->next, pb = Lb->next, pc;Lc = pc = La;//同时遍历链表La和Lbwhile (pa && pb){if (pa->data > pb->data){pc->next = pb;pb = pb->next;}else{pc->next = pa;pa = pa->next;}pc = pc->next;}pc->next = pa ? pa : pb;// 插入剩余段   free(Lb); // 释放Lb的头结点   Lb=NULL;}


3. 不带头结点的单链表


3.1 不带头结点的单链表的存储结构(和带头结点的一样)
typedef struct LNode{ElemType data;struct LNode        *next;}LNode, *LinkList;

3.2 不带头结点的单链表的基本操作


   不带头结点的单链表的基本操作和带头结点单链表差不多,插入和删除操作有点不一样

(1)插入和删除

// 在带头结点的单链线性表L中第i个位置之前插入元素eStatus InsertList(LinkList &L, int i, ElemType e){intj = 1;LinkListp = L, pnew;if(i<1) // i值不合法    return ERROR;    pnew = (LinkList)malloc(sizeof(LNode));if (!pnew)exit(OVERFLOW);pnew->data = e;           if (1 == i){pnew->next = L;L = pnew; // 改变L}else{// 寻找第i-1个结点while (j < i-1 && p){p = p->next;j++;}if (!p) // i大于表长return ERROR;pnew->next = p->next;p->next = pnew;}return OK;}// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值Status DeleteList(LinkList &L, int i, ElemType &e){intj = 1;LinkListp = L, q;    if (1 == i){L = p->next;// L由第2个结点开始e = p->data;free(p);}else{// 寻找第i个结点,并令p指向其前驱while (j < i-1 && p->next){p = p->next;j++;}if(!p->next || j>i-1) // 删除位置不合理return ERROR;q = p->next;e = q->data;p->next = q->next;free(q);}return OK;}

(2)头插法和尾插法创建单链表

// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表Lvoid HeadCreList(LinkList &L, int n){LinkList head;L = NULL;printf("please input %d element.\n", n);for (int i = 0; i < n; i++){head = (LinkList)malloc(sizeof(LNode));if (!head) exit(OVERFLOW);scanf("%d", &(head->data));head->next = L; // 插入到表头L = head;}}// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表Lvoid TailCreList(LinkList &L, int n){LinkList temp, tail;printf("please input %d element.\n", n);for (int i = 0; i < n; i++){temp = (LinkList)malloc(sizeof(LNode));if (!temp) exit(OVERFLOW);scanf("%d", &(temp->data));if (0 == i){L = tail = temp;}else{tail->next = temp;tail = tail->next;  // 总是让tail指向链表的尾结点}}tail->next = NULL;// 不要忘记将尾结点的next指针置为空}
(3)两个算法

// 将所有在线性表Lb中但不在La中的数据元素插入到La中void Union(LinkList &La, LinkList Lb){int la_len = ListLength(La);LinkList pa = La, pb = Lb;while (pb){if (!LocateElem(La, pb->data, compare))InsertList(La,++la_len, pb->data);pb = pb->next;}}// 已知单链线性表La和Lb的元素按值非递减排列。// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列void MergeList(LinkList La, LinkList Lb, LinkList &Lc){LinkList pa = La, pb = Lb, pc;int isfirst = 1;//同时遍历链表La和Lbwhile (pa && pb){if (isfirst){if (pa->data > pb->data){Lc = pc= pb;pb = pb->next;}else{Lc = pc = pa;pa = pa->next;}isfirst = 0;}else{if (pa->data > pb->data){pc->next = pb;pc = pb;pb = pb->next;}else{pc->next = pa;pc = pa;pa = pa->next;}}}pc->next = pa ? pa : pb;// 插入剩余段}