链表:
声明: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 8
1 3 2 3 4 5 6 7 8
1 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不合法,这时我们不进行插人,所以不会给新结点申请空间