链表:
声明:LNode* LinkList在链表里面这两个是等价的;
#include<stdio.h>
#include<stdlib.h>
typedef int Elemtype;
typedef struct LNode{Elemtype data;struct LNode* next;
}LNode,*LinkList;
链表打印函数:
//打印void Print(LinkList L){L=L->next;while(L){printf("%3d",L->data);L=L->next;}printf("\n");}
头插法新建链表:
定义链表头指针--》头结点申请空间--》scanf读取第一个值--》while循环读取
//头插法建立新链表LinkList list_head(LinkList&L){LNode *s;int x;L=(LinkList)malloc(sizeof(LNode));//带头结点的链表L->next=NULL;scanf("%d",&x);while(x!=9999){s=(LinkList)malloc(sizeof(LNode));s->data=x;s->next=L->next;L->next=s;scanf("%d",&x);} return L;}int main(){LinkList L;list_head(L);Print(L);return 0;}
头插法:带头结点的链表,头结点的位置是不动的,头插法新建链表,每次添加的新节点是放在第一个结点前,并非头结点前。所以输出是逆序的。
cin:3 3 7 8 9999
cout:8 7 3 3
申请空间:申请空间malloc返回的是void* 指针,因此我们需要强制转化为(LNode*),但是空间的大小必须是结构体的大小才可以,所以写法是(即前面有幸后无形)
s=(LNode*)malloc(sizeof(LNode));
或者
s=(LinkList)malloc(sizeof(LNode));
尾插法:
LinkList tail_insert(LinkList&L){int x;L=(LNode*)malloc(sizeof(LNode));//带头结点的链表LNode* s;LNode* r=L;scanf("%d",&x);while(x!=9999){s=(LinkList)malloc(sizeof(LNode));s->data=x;r->next=s;r=s;scanf("%d",&x);} r->next=NULL;return L;}
尾插法新建链表,需要使用一个辅助指针r ,始终指向尾部,从而避免每次尾插都需要从头开始。
使得复杂度为0(1)
结点顺序,同输入顺序相同。
cin:11 2 45 9999
cout: 11 2 45
查找:按值查找,按序查找 时间复杂度都是0(n)
//按序号查找,返回第i个 LinkList GetElem(LinkList L,int i){if(i==0) return L;if(i<0) return NULL;int j=1;LNode*p=L->next;while(p&&j<i){p=p->next;j++;}if(!p)return NULL;return p;} //按值查找LinkList GetNum(LinkList L,int a){LNode *p=L->next;while(p&&p->data!=a){if(p->data==a) return p;p=p->next;}return NULL;} //main()函数插入
LNode* elem=GetElem(L,2);if(elem) printf("%d ",elem->data);else printf("NULL ");LNode* num=GetNum( L,3);if(num) printf("%d",num->data);else printf("NULL ");
注意:
1、按位置查找时,头结点一般不放值,所以按值查找从第一个结点遍历。头结点是位置 0,头结点之后的结点,是第一个结点。如果链表内元素是3,4,5,6,7,那么我们最多只能查第5个结点
2、单链表只能从前往后遍历,不能像顺序表那样拿某个位置可以直接到达,顺序表的按位置获取元素时间复杂度是0(1),但是链表位置查找的时间复杂度是 0(n),链表按值查找的时间复杂度是 O(n)
插入 &删除 主要就是断链问题
//在第i个位置插入元素 bool insert_i(LinkList &L,int i,int a){if(i<1) return false;LNode *pre=GetElem(L,i-1);if(!pre) return false; LNode *p=(LNode*)malloc(sizeof(LNode));p->data=a;p->next=pre->next;pre->next=p;return true;} //在第i个位置删除元素 bool delete_i(LinkList &L,int i){if(i<0) return false;LNode *pre=GetElem(L,i-1);if(!pre) return false;LNode *p=pre->next;pre->next=p->next;free(p);return true;} //main()插入
bool insert=insert_i(L,2, 3);if(insert) Print(L);else printf("NULL ");bool delet=delete_i(L,2);if(delet) Print(L);else printf("NULL ");
cin:1 2 3 4 5 6 7 8 9999
cout: 1 2 3 4 5 6 7 81 3 2 3 4 5 6 7 81 2 3 4 5 6 7 8
注意:
1.往第i个位置插人,首先我们需要 GetElern 两数,拿到第i-1 个位置元素的地址,这样我们才能把要插人的元素放到 i-1 元素的后面,从而成为第i个元素
2.链表有5个元素时,我们只能插人到 1-6 位置,不能插人到第 10个位置了。可以通过 GetElem() 函数实现:获取第 i-1 个元素的地址时,如果返回的是 NULL,代表插人位置i不合法,这时我们不进行插人,所以不会给新结点申请空间
作业:王道OJ | 课时11作业 (lgwenda.com)