顺序表的操作
这一部分比较简单,相信只要系统学过C语言的大佬应该很容易理解,先直接上代码吧
以英语字母线性表为例:
char V[30];
void build(char* v[])
/*字母线性表生成*/
{ int i;
V[0]='a';
for( i=1; i<=n-1; i++ )
V[i]=V[i-1]+1;
}
void display( char* v[])
/*字母线性表的显示*/
{ int i;
for( i=0; i<=n-1; i++ )
printf( "%c", v[i] );
printf( "\n " );
}
void delete(char* v[n],int k) //删除v[n]表里的第k个(下标为k-1)。
/*字母的删除*/
{
int i;
for(i=k-1;i<n;i++)
v[i]=v[i+1];
n--;//表长减一
}
void insert (int k,int l)
/*在k处插入数l(假设不会越界)*/
{
int i;
for(i=n;i>=k;i--)
v[i+1]=v[i]
v[i]=l;
n++;//表长加一
}
线性表存储特点
- 逻辑上相邻的元素,其物理上也相邻。
- 若已知表中某个元素在存储器中的位置,则其他元素存放位置亦可求出(利用数组a[n]的下标)。
设loc(a[n])表示a[n]在存储器中的首地址,L表示单个数据元素所占地址长度:则满足
*loc(a[n+k])=loc(a[n])+k*L 和 loc(a[n])=loc(a[1])+(n-1)L
动态顺序表
这个主要是realloc(p,newsize)函数的应用,在此只引用书上内容解释下realloc的用法,不给出其他详细代码了:
realloc (p, newsize)函数的意思是:新开一片大小为newsize的连续空间,并把以p为首址的原空间数据都拷贝进去。
这是书上的一段伪代码:
Status ListInsert_Sq(SqList &L, int i, ElemType e)
{ //在顺序线性表中第i个位置之前插入新的元素e
if( i < 1 or i > L.length+1) return ERROR; // 检验i 值的合法性
if ( L.length ≥ L.listsize ) //若表长超过表尺寸则要增加尺寸
{ newbase = ( ElemType* ) realloc ( L.elem , (L.listsize + LISTINCREMENT )* sizeof ( ElemType ) );
if (newbase=NULL ) exit( OVERFLOW ) ; //分配失败则退出并报错
L.elem = newbase ; //重置新基址
L.listsize = L.listsize + LISTINCREMENT ; } //增加表尺寸
链表操作
链表存储特点
结点在存储器中的位置是随意的,即逻辑上相邻的数据元素在物理上不一定相邻。
为描述存储形式,引用一个图片:
链表的结点结构体代码:
typedef struct Lnode {
ElemType data; //数据域
struct Lnode *next; //指针域
}Lnode, *LnodeList; // *LnodeList为Lnode类型的指针
链表中有头指针、头结点、首元结点的概念:
头指针:是指向链表中第一个结点(或为头结点、或为首元结点)的指针;通常头指针也是一个类似于结点的结构体
头结点:是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息,它不计入表长度。
首元结点:是指链表中存储线性表第一个数据元素a1的结点。
关于单链表的代码
- 创建单链表
/*创建单链表,还是存储26个字母为例*/
LnodeList build()
{
LnodeList head,p,q;
head=(LnodeList)malloc(sizeof(Lnode));
p=head;
q=head;
for( i=1; i<26; i++) //因尾结点要特殊处理,故i≠26
{
p->data=i+‘a’-1; // 第一个结点值为字符a
q=(node*)malloc(sizeof(Lnode)); //为后继结点开辟空间
p->next=q;
p=q;
} //让指针变量p指向后一个结点
p->data=i+‘a’-1; //最后一个元素要单独处理让其next指向空
p->next=NULL ;}
return head;
}
- 插入
/*在第i个元素后插入j元素*/
void insert(LnodeList head,int i,char j)
{
int k;
LnodeList p,r;
p=head;//若带头结点则为 p=head->next;
while(p&&k<i)
{
p=p->next;
k++;
}if(!p) return error;
r=(LnodeList)malloc(sizeof(Lnode));
r->data=j;
r->next=p->next;
p->next=r;
}
- 修改
/*把第i个元素关键字改成j*/
void change(LnodeList head,int i,char j)
{int k;
LnodeList p,r;
p=head;//若带头结点则为 p=head->next;
while(p&&k<i)
{
p=p->next;
k++;
}if(!p) return error;
p->data =j; //读取第i个结点的代码仅需要把这句改成:printf("%c",p->data);
}
- 删除
/*把第i个元素删除*/
void delete(LnodeList head,int i,char j)
{int k;
LnodeList p,q;
p=head;//若带头结点则为 p=head->next;
while(p&&k<i-1)
{
p=p->next;
k++;
}
q=p->next;
if(!p&&!q) return error;
p->next=q->next;
free(q);
- 逆置
void InverseList(LnodeList head){
LnodeList p,q;
p=head;//若带头结点则以下代码中head都改成head->next
head=NULL;
while(p){
q=p;
p=p->next;
q->next=head;
head=q;
}//while
}
下图是逆置的代码一部分的图示:
这里引用一段循环单链表的逆置核心算法
/*循环单链表的逆置核心语句*/
//替换法
q=head;//逆置后的第一个结点
p=head->next; //有头结点
while(p!=head) //循环单链表
{ r=p->next;
p->next=q; //前驱变后继
q=p;
p=r; } //准备处理下一结点
head->next=q; // 以an为首
//插入法
p=head->next; //有头结点
if(p!=head){r=p->next;
p->next =head;p=r}; //处理a1
while(p!=head) //循环单链表
{ r=p ->next //保存原后继
p ->next= head->next;
head->next=p;
p=r;} //准备处理下一结点
- 两个单链表合并(并且按照从小到大的顺序)
/*原链表head1和head2都有序*/
LnodeList p,q,r,s,t;
p=head1;
q=head2; //head1和head2都有头结点
r=p->next;
s=q->next;
while(p&&q) //归并到head2去
{
if(r->data>s->data)
{p->next=s;
q->next=p;
q=s;
s=s->next;
p=r;
r=r->next;}
else
{t=p;
p=r;
r=r->next;
p->next=s->next;
s->next=p;
t->next=r;
p=t;
s=s->next;
q=q->next;
}
if (p&&!q)
s->next=p;
}
以上代码是单纯的指针方向改变的运算,可能不完善、有bug,还请各位大佬指正,下面给出另一种算法
LnodeList p1,p2,head3,p3;
p1=head1->next;
p2=head2->next;
head3=pc=head1; //有头结点
while(p1&&p2) //将p1 、p2结点按大小依次插入head3中
{ if(p1->data<=p2->data)
{p3->next=p1;
p3=p1;
p1=p1->next;}
else {p3->next=p2;
p3=p2;
p2=p2->next}
}
p3->next = p1 ? p1: p2 ; //插入非空表的剩余段
free(head2); //释放head2的头结点
- 单链表的直接插入排序运算
这个算法的思想我参考了插入排序的思想,祥见:插入排序
r=head;
q=head->next;
while(q)
{
p=head;
if(q->data<r->data)
{t=q->next;
while(p->data<=q->data)
p=p->next;
q->next=p->next;
p->next=q;
q=t;
}
else
{
r=q;
q=q->next;
}
}
双链表
结构体:
typedef struct tnode {
ElemType data; //数据域
struct tnode *next,*last; //指针域
}tnode, *tnodeList; // *tnodeList为tnode类型的指针
- 创建双链表
/*创建单链表,还是存储26个字母为例*/
LnodeList build()
{
tnodeList head,p;
head=(tnodeList)malloc(sizeof(tnode));
p=head;
q=head;
for( i=1; i<26; i++) //因尾结点要特殊处理,故i≠26
{
p->data=i+‘a’-1; // 第一个结点值为字符a
q=(node*)malloc(sizeof(tnode)); //为后继结点开辟空间
q->last=p;
p->next=q;
p=q;
} //让指针变量p指向后一个结点
p->data=i+‘a’-1; //最后一个元素要单独处理让其next指向空
p->next=NULL ;}
return head;
}
- 插入操作:
/*在第i个元素后插入j元素*/
void insert(tnodeList head,int i,char j)
{
int k;
tnodeList p,q,r;
p=head;//若带头结点则为 p=head->next;
while(p&&k<i)
{
p=p->next;
k++;
}if(!p) return error;
q=p->next;
r=(LnodeList)malloc(sizeof(Lnode));
r->data=j;
r->next=p->next;
r->last=q->last;
p->next=r;
q->last=r;
- 删除操作:
/*删除第i个元素*/
void insert(tnodeList head,int i,char j)
{
int k;
tnodeList p,q,r;
p=head;//若带头结点则为 p=head->next;
while(p&&k<i-1)
{
p=p->next;
k++;
}if(!p) return error;
q=p->next;
r=q->next;
p->next=r;
r->last=p;
free(q);
以上是笔者面临期末考试心血来潮写的一些《数据结构》知识点,算是一个总结吧,之后的章节不一定会有空写了,部分代码可能会有bug还请各位大佬多多指正。