线性表的链式存储。主要是单链表的相关知识,介绍了正序建立单链表、逆序建立单链表、单链表的插入、删除、查找、输出以及单链表的合并方法。单链表的合并前提是两个都有序。具体知识点详见代码注释。
1 /*** 2 线性表的链式存储结构不能随机存储,整个链表的存取都必须从头结点开始。但是没有顺序存储的缺点:插入或者删除时需要移动大量元素 3 其特点是用一组任意的存储空间存储线性表的数据元素 4 5 结点:数据域和指针域 6 */ 7 #include<stdio.h> 8 #include<stdlib.h> 9 #define MAX 100000 10 //**********************线性表的单链表存储结构*********************** 11 typedef struct LNode 12 { 13 int data; //数据域 14 struct LNode *next; //指针域 15 } LNode,*LinkList; //LinkList是LNode型结构的指针 16 17 18 //**************************头插法逆序建立单链表**************************** 19 void CreateList_L1(LinkList &L,int n) 20 { 21 //逆位序输入n个元素值,建立带有头结点的单链表,时间复杂度为O(n) 22 L->next = NULL;//先建立个带有头结点的单链表L,使头结点指针域为空, 23 for (int i = n; i > 0; --i) 24 { 25 LinkList p = (LinkList)malloc(sizeof(LNode));//生成新结点 26 scanf("%d",&p->data); 27 //插入到表头 28 p->next = L->next; 29 L->next = p; 30 } 31 32 } 33 34 //**************************尾插法正序建立单链表**************************** 35 void CreateList_L2(LinkList &L,int n) 36 { 37 //借助一个中间变量,正序建立n个元素的单链表,时间复杂度仍然为O(n) 38 //先建立个带有头结点的单链表L,头结点指针域为空,默认使用Linklist建立L是即为空 39 LinkList r = L; 40 for (int i = 0; i < n; ++i) 41 { 42 LinkList p = (LinkList)malloc(sizeof(LNode));//生成新结点 43 scanf("%d",&p->data); 44 //插入到表尾 45 r->next = p; 46 r = p; 47 } 48 r->next = NULL; 49 } 50 51 //**************************按序输出单链表的各个元素**************************** 52 void PrintList_L(LinkList L) 53 { 54 LinkList p = L->next;//p指向第一个元素 55 while(p) 56 { 57 printf("%d ", p->data); 58 p = p->next; 59 } 60 } 61 62 //***********************取出指定位序的单链表中的数据元素*********************** 63 int GetElem_L(LinkList L,int i) 64 { 65 //时间复杂度为O(n) 66 //L为带头结点的单链表的头指针 67 //返回链表的第i个元素,若没有,则错误提示 68 LNode *p = L->next; //初始化,p指向第一个结点 69 int j = 1;//计数器 70 int e;//返回值 71 while(p && j<i) 72 { 73 //顺指针向后查找,知道p指向第i个元素或者p为空 74 p = p->next; 75 ++j; 76 } 77 if (!p || j>i) 78 { 79 printf("没有找到该元素,请确认后重新输入位序!\n"); 80 e = MAX; 81 return e; 82 } 83 e = p->data; 84 return e; 85 } 86 87 88 //*******************向单链表的指定位序插入一个元素******************** 89 void ListInsert_L(LinkList &L,int i,int e) 90 { 91 //在带头结点的单链表L的第i个位置之前插入元素e,1=<i<=length(L)+1,时间复杂度为O(n) 92 /* 93 LNode *p = L->next; //初始化,p指向第一个结点,下面一句话效果一样,因为LinkList是指向LNode型的指针变量,无需再加*号 94 LinkList p = L->next; 95 int j = 1;//计数器 96 */ 97 //上面使p指向第一个结点的方法有问题,如果要将元素插在第一位会有bug,故要从头结点开始 98 LinkList p = L; 99 int j = 0; 100 while(p && j<i-1) 101 { 102 p = p->next; //寻找第i-1个结点,使p指向它 103 ++j; 104 } 105 if (!p || j>i-1) 106 { 107 printf("要插入的位置没有找到,可能是插入位序有误!\n"); 108 getchar(); 109 exit(1); 110 } 111 LinkList s = (LinkList)malloc(sizeof(LNode));//生成一个结点s 112 s->data = e; 113 //插入结点 114 s->next = p->next; 115 p->next = s; 116 } 117 118 //********************删除单链表的指定位序的一个元素******************** 119 void ListDelete_L(LinkList &L,int i) 120 { 121 //时间复杂度为O(n) 122 //在带头结点的单链表L中,删除第i个位置的元素.i<=length(L) 123 LNode *p = L; //初始化,p指向头结点 124 int j = 0;//计数器 125 while(p->next && j<i-1) 126 { 127 p = p->next; //使p指向被删除结点的前一个结点,即p指向i-1 128 ++j; 129 } 130 if (!(p->next) || j>i-1) 131 { 132 //要保证p的后一个元素有值,即被删除元素存在 133 printf("删除位置不合理,请重新确认!\n"); 134 getchar(); 135 exit(1); 136 } 137 //删除该元素 138 LNode *q = p->next; 139 p->next = q->next; 140 free(q);//释放结点 141 } 142 143 //**************************有序链表的合并****************************** 144 void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc) 145 { 146 //已知单链线性表La和Lb的元素按值非递减排序 147 //归并La和Lb得到新的单链表Lc,Lc的元素也按值非递减排列 148 LinkList pa,pb,pc; 149 pa = La->next; 150 pb = Lb->next; 151 //用La的头结点作为Lc的头结点 152 pc = La; 153 Lc = pc; 154 while(pa && pb) 155 { 156 if (pa->data <= pb->data) 157 { 158 pc->next = pa; 159 pc = pa; 160 pa = pa->next; 161 } 162 else 163 { 164 pc->next = pb; 165 pc = pb; 166 pb = pb->next; 167 } 168 } 169 pc->next = pa?pa:pb; 170 free(Lb);//释放Lb的头结点 171 } 172 173 int main() 174 { 175 //定义3个链表,初始化3个头结点 176 LinkList la = (LinkList)malloc(sizeof(LNode)); 177 LinkList lb = (LinkList)malloc(sizeof(LNode)); 178 LinkList lc;// 179 int count;//计数使用,链表初始化时记录链表元素个数 180 int location,value;//插入时的位置和值 181 char c; 182 //使用头插法逆序建立链表 183 printf("请输入la链表元素个数:"); 184 scanf("%d",&count); 185 CreateList_L1(la,count); 186 printf("逆序建立的la链表元素为:"); 187 PrintList_L(la); 188 189 //使用尾插法正序建立链表 190 printf("\n\n请输入链表lb元素个数:"); 191 scanf("%d",&count); 192 CreateList_L2(lb,count); 193 printf("正序建立的lb链表元素为:"); 194 PrintList_L(lb); 195 196 //向正序建立的单链表中插入数据 197 printf("\n\n请输入lb插入位置和插入值:"); 198 scanf("%d%d",&location,&value); 199 ListInsert_L(lb,location,value); 200 printf("插入元素之后的链表元素为:"); 201 PrintList_L(lb); 202 203 //继续测试该链表的删除效果 204 printf("\n\n请输入lb删除位置:"); 205 scanf("%d",&location); 206 ListDelete_L(lb,location); 207 printf("删除元素之后的链表元素为:"); 208 PrintList_L(lb); 209 210 //测试GetElem函数是否有效 211 printf("\n\n请输入需要得到的元素位置:"); 212 scanf("%d",&location); 213 value = GetElem_L(lb,location); 214 if (value == MAX) 215 { 216 printf("输入错误或者没有该位置的元素!\n"); 217 }else 218 { 219 printf("该元素的值为:%d",value); 220 } 221 222 //测试两个链表的归并,前提是两个链表都有序 223 MergeList_L(la,lb,lc); 224 printf("\n\n两个链表合并后lc的元素是:"); 225 PrintList_L(lc); 226 227 return 0; 228 }