顺序存储结构如数组,进行插入与删除操作,往往会比较复杂,因为牵扯到了大量数组元素的后移操作,导致算法时间复杂度很高,但是链式存储结构的很好的解决了这一问题,链表在进行插入与删除操作时,只需遍历一遍线性表就可以搞定。缺点是不能进行随机访问,并且交换两个元素十分麻烦,往往指针会混乱,同样也不推荐将需要排序的数据使用链式结构储存,用顺序存储结构则更为轻松。
下面是单链表的定义及其操作的c++代码
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef int ElemType;
typedef struct _NODE
{
ElemType data;
struct _NODE* next;
}NODE;
typedef NODE* LinkList;
/*
函数名 :GetElem
功能描述 :用e返回L中第i的元素
输入 :sl,e,i
sl,已存在顺序表
输出 :成功返回bool类型,true,否则返回false
全局变量 :无
调用模块 :
作者 :zhang·haochen
日期 :2019 - 01 - 19
修改 :
修改日期 :
版本 :ver 1.0
*/
bool GetElem(LinkList s1,ElemType* e,int i)
{
NODE* Ppos = s1;
int j = 0;
while (Ppos&&j < i)
{
Ppos = Ppos->next;
j++;
}
if (Ppos==NULL)
return false;
*e = Ppos->data;
return true;
}
/*
函数名 :ListInsert
功能描述 :在线性表第i个位置插入新元素e
输入 :sl,e,i
sl,已存在顺序表
输出 :插入成功返回bool类型,true,否则返回false
全局变量 :无
调用模块 :
作者 :zhang·haochen
日期 :2019 - 01 - 19
修改 :
修改日期 :
版本 :ver 1.0
*/
bool LinkListInsert(LinkList* s1,int i,ElemType e)
{
//插入位置小于0,错误的插入位置
if (i < 0)
return false;
//插入位置为0,头插
if (i == 0)
{
NODE* pnew = (NODE*)malloc(sizeof(NODE));
pnew->data = e;
pnew->next = *s1;
*s1 = pnew;
return true;
}
//判断插入位置是否合法
NODE* pPos = *s1;
//(重点)寻找插入位置的前一个结点比如插入位置为5,则pPos从0往后移动4次,找到4(5的前一个).
for (int j = 1; j < i&&pPos != NULL;j++)
{
pPos = pPos->next;
}
//判断i的位置是否合法
if (pPos == NULL)
return false;
//合法时pPos不为空,且是i的前一个
NODE* pnew = (NODE*)malloc(sizeof(NODE));
pnew->data = e;
pnew ->next = pPos->next;
pPos->next = pnew;
return true;
}
/*
函数名 :LinkListDelete
功能描述:删除单链表L中第i个位置的元素
输入 :L,i
L,已存在顺序表
i,删除位置,范围 0 <= i < 链表长度
输出 :bool值,删除成功true,否则false
全局变量:无
调用模块:无
作者 :zhang·haochen
日期 :2019-01-19
修改 :
修改日期:
版本 :ver 0.1
*/
bool LinkListDelete(LinkList* s1,int i)
{
//若删除位置小于0,则删除失败
if (i<0)
return false;
//删除位置为0,为头删
if (0 == i)
{
NODE* pPos = *s1;
*s1 = pPos->next;
free(pPos);
pPos = NULL;
return true;
}
//寻找待删除节点的前一个节点
else
{
NODE* pPos = *s1;
for (int j = 1; j < i&&pPos != NULL; j++)
{
pPos = pPos->next;
}
//找不到
if (pPos == NULL || pPos->next == NULL)
return false;
NODE* pnew = (NODE*)malloc(sizeof(NODE));
NODE* t;
t = pPos->next;
pPos->next = t->next;
free(t);
t = NULL;
return true;
}
}
/*
函数名 :ClearLinkList
功能描述:清空链表
输入 :L
L,已存在顺序表
输出 :
全局变量:无
调用模块:LinkListDelete
作者 :zhang·haochen
日期 :2019-01-19
修改 :
修改日期:
版本 :ver 0.1
*/
void ClearLinkList(LinkList* s1)
{
while (*s1)
{
LinkListDelete(s1, 0);
}
}
void ShowLinkList(const LinkList s1)
{
NODE* pPos = s1;
while (pPos!=NULL)
{
printf("%d,",pPos->data);
pPos = pPos->next;
}
puts("\b;");
}
/*
函数名 :LinkListReverse
功能描述:清空链表
输入 :s1
s1,已存在顺序表
输出 :
全局变量:无
调用模块:
作者 :zhang·haochen
日期 :2019-01-20
修改 :
修改日期:
版本 :ver 0.1
*/
void LinkListRerverse(LinkList* s1)
{
if (*s1 == NULL || (*s1)->next == NULL)
return;
NODE *p1 = *s1;
NODE *p2 = p1->next;
NODE *p3 = p2->next;
p2->next = p1;
while (p3 != NULL)
{
p1 = p2;
p2 = p3;
p3 = p3->next;
p2->next = p1;
}
(*s1)->next = NULL;
*s1 = p2;
}
int main()
{
LinkList s1 = NULL;
LinkListInsert(&s1, 0, 1);
LinkListInsert(&s1, 1, 2);
LinkListInsert(&s1, 2, 3);
LinkListInsert(&s1, 3, 4);
LinkListInsert(&s1, 4, 5);
LinkListInsert(&s1, 5, 6);
LinkListInsert(&s1, 6, 7);
LinkListInsert(&s1, 7, 8);
LinkListDelete(&s1, 0);
ShowLinkList(s1);
LinkListRerverse(&s1);
ShowLinkList(s1);
return 0;
}
下面举上几个往年公司可能出的笔试题
1.求出单链表中的倒数第四个元素
思路,可以定义四个链表节点指针,分别指向第0,1,2,3个节点,然后同时往后遍历,直到,第四个指针的next域为NULL,则此时第一个指针所指的节点为倒数第四个节点。
ElemType FindLast4th(LinkList* l1)
{
if (*l1 == NULL || (*l1)->next == NULL || (*l1)->next->next == NULL || (*l1)->next->next->next == NULL)
return -1;
NODE* p1 = *l1;
NODE* p2 = *l1;
p2 = p2->next;
p2 = p2->next;
p2 = p2->next;
while (p2->next!=NULL)
{
p1 = p1->next;
p2 = p2->next;
}
return p1->data;
}
int main()
{
LinkList s1 = NULL;
ElemType e;
LinkListInsert(&s1, 0, 5);
LinkListInsert(&s1, 0, 4);
LinkListInsert(&s1, 0, 3);
LinkListInsert(&s1, 0, 2);
LinkListInsert(&s1, 0, 1);
ShowLinkList(s1);
printf("倒数第四个为:%d\n",FindLast4th(&s1));
return 0;
}
2.单链表交换任意两个元素(不包括表头)
思路,交换第i,j个元素,定义两个链表节点指针,并都把头指针赋给他们,根据需要交换的两个下标,分别找出他们的前继节点i-1和j-1,在定义四个变量分别用来保存i节点与i+1节点,j节点与j+1节点。然后进行判断当i与j相邻时(即j-i=1或i-j=1),一种操作,其余情况在另一种操作中。具体操作见代码。
bool SwapLinkList(LinkList* s1,int i,int j)
{
if (i == 0 || j == 0)
return false;
NODE* prepPosi = *s1;
NODE* prepPosj = *s1;
for (int k = 1; k < i; k++)
{
prepPosi = prepPosi->next;
}
for (int k = 1; k < j; k++)
{
prepPosj = prepPosj->next;
}
NODE* pi, *pj,*posti,*postj;
pi = prepPosi->next;
pj = prepPosj->next;
posti = pi->next;
postj = pj->next;
if (j - i == 1)
{
prepPosi->next = pj;
pj->next = pi;
pi->next = postj;
return true;
}
if (i - j == 1)
{
prepPosj->next = pi;
pi->next = pj;
pj->next = posti;
return true;
}
prepPosi->next = pj;
pj->next = posti;
prepPosj->next = pi;
pi->next = postj;
return true;
}
3.大数相加(就是计算机不能表示的整数(往往有很多位),进行相加)
思路首先这两个大数使用2个挺大的字符数组存储,然后比较两个数的长度,较短的那个数,相差几位,就往前面补几个0;然后使用链表头插,存进事先创建的链表中。大概就完成了,具体看代码。
/*
函数名 :AddBigNum
功能描述 :非常大的数相加
输入 :L2,数1和数1的长度,数2和数2的长度L2,已存在空链表表
输出 :
全局变量 :无
调用模块 :
作者 :zhang·haochen
日期 :2019 - 01 - 22
修改 :
修改日期 :
版本 :ver 0.1
*/
void AddBigNum(char* s1,int len1,char* s2,int len2,CLinkList** l2)
{
//定义进位数为p,且初值为0;
int p = 0;
char c;
if (len1 > len2)
{
int news2 = len2+len1-len2;
for (int oldlen2=len2; news2 >=0; news2--,oldlen2--)
{
if (news2 > len1-len2-1)
{
s2[news2] = s2[oldlen2];
}
else
s2[news2] = '0';
}
}
else if (len1 < len2)
{
int news1 = len1+len2-len1;
for (int oldlen1=len1; news1 >=0; news1--,oldlen1--)
{
if (news1 > len2-len1-1)
{
s1[news1] = s1[oldlen1];
}
else
s1[news1] = '0';
}
}
int i = strlen(s1) - 1, j = strlen(s2) - 1;
while (i >= 0 && j >= 0)
{
c= (((s1[i]-'0') +( s2[j]-'0' )+p) % 10)+'0';
p = ((s1[i--]-'0') +( s2[j--]-'0')+p) / 10;
LinkListInsert2(l2,0,c); //头插
}
if (p == 1) //最多会一位,即最多前面多个1
{
LinkListInsert2(l2, 0, '1'); //头插
}
}
int main()
{
CLinkList* l2 = NULL;
char s1[100] = "6658142543636282449368";
char s2[100] = "64531546346853468453468";
int len1 = strlen(s1);
int len2 = strlen(s2);
AddBigNum(s1,len1, s2,len2, &l2);
ShowCLinkList2(l2);
return 0;
}
上述各类函数,都由本人多次测试过,没出现问题。有疑问的小伙伴请评论问题,看到会尽快回复的。