一、线性表(包括栈,队列)
文章目录
1、备注
- 链表的基本操作(遍历,删除,插入),都离不开pre,current,r(或者更多指针)的同步移动问题,主要考察针线活的细心熟练程度。
2、节点定义
Ⅰ、链表节点定义
typedef struct LNode
{
int data;
struct LNode *next;
} LNode;
Ⅱ、栈节点定义
//栈
typedef struct Stack
{
int data[maxsize];
int top;
} Stack;
void InitStack(Stack *stack);
void push(Stack *stack, int x);
int pop(Stack *stack);
bool isEmpty(Stack *stack);
void InitStack(Stack *stack)
{
stack->top = -1;
}
void push(Stack *stack, int x)
{
stack->data[++stack->top] = x;
}
int pop(Stack *stack)
{
return stack->data[stack->top--];
}
bool isEmpty(Stack *stack){
if(stack->top == -1){
return true;
}
return false;
}
Ⅲ、队列结点定义
- 非循环队列(可能会溢出)
typedef struct Queue
{
ElemType *data[maxsize]; //指针数组
int front, rear;
} Queue;
void InitQueue(Queue *queue);
void Enqueue(Queue *queue, ElemType *x);
ElemType *Dequeue(Queue *queue);
bool isQueueEmpty(Queue *queue);
void InitQueue(Queue *queue)
{
queue->front = -1;
queue->rear = -1;
}
void Enqueue(Queue *queue, ElemType *x)
{
queue->data[++queue->rear] = x;
}
ElemType *Dequeue(Queue *queue)
{
return queue->data[++queue->front];
}
bool isQueueEmpty(Queue *queue)
{
if (queue->rear == queue->front)
{
return true;
}
return false;
}
- 循环队列
#define maxsize 6
typedef struct{
ElemType data[maxsize];
int rear,front;
}Quene;
void InitQueue(Queue *queue){
queue->front=0;
queue->rear=0;
}
void Enqueue(Queue *queue,ElemType x){
if(!isQueueFull(queue)){
queue->rear = (queue -> rear + 1) % maxsize;
queue->data[queue->rear] = x;
}
}
ElemType Dequeue(Queue *queue){
if(!isQueueEmpty){
queue->front = (queue->front + 1) % maxsize;
return queue->data[queue->front];
}
}
bool isQueueFull(Queue *queue){
if((q->rear + 1) % maxsize == q->front){
return true;
}
return false;
}
bool isQueueEmpty(Queue *queue){
if(queue->first == queue->rear){
return true;
}
return false;
}
3、遍历操作:
Ⅰ、Demo 1:反向输出
1)描述:
- 设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值
2)算法思想:
- 将链表中的值压入栈中,最后反向输出
void reversePrint(LNode *head)
{
if (head == NULL)
{
return;
}
Stack stack;
InitStack(&stack);
LNode *current = head->next;
while (current != NULL)
{
push(&stack, current->data);
current = current->next;
}
while(!isEmpty(&stack)){
printf("%d ",pop(&stack));
}
printf("\n");
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *head = (LNode *)malloc(sizeof(LNode));
r = head;
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 1;
r->next = node;
r = node;
}
}
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 4;
r->next = node;
r = node;
}
}
for (i = 1; i < 4; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 9;
r->next = node;
r = node;
}
}
r->next = NULL;
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//反向输出链表
reversePrint(head);
printf("\n");
system("pause");
}
Ⅱ、Demo 2:找公共节点
1)描述:
- 给定两个单链表,编写算法找出两个链表的公共结点
2)算法思想:
- 先比较两个链表A,B的长度为lengthA,lengthB
- 长的链表需要比短的链表多走|lengthA - lengthB|,之后再同步走
- 若遇到同一个结点,则结束
LNode *search_Common(LNode *head1, LNode *head2)
{
if (head1 == NULL || head2 == NULL)
{
return NULL;
}
int lengthA = 0, lengthB = 0;
LNode *p1 = head1, *p2 = head2;
//计算两链表的长度
while (p1 != NULL)
{
lengthA += 1;
p1 = p1->next;
}
while (p2 != NULL)
{
lengthB += 1;
p2 = p2->next;
}
p1 = head1, p2 = head2;
int substract = abs(lengthA - lengthB);
if (lengthA > lengthB)
{
while (substract > 0)
{
p1 = p1->next;
substract--;
}
}
if (lengthB > lengthA)
{
while (substract > 0)
{
p2 = p2->next;
substract--;
}
}
//p1,p2同时走
while (p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
int main()
{
int i;
LNode *r1 = NULL, *r2 = NULL; //指向链表队尾的指针
LNode *head1 = (LNode *)malloc(sizeof(LNode));
LNode *head2 = (LNode *)malloc(sizeof(LNode));
r1 = head1, r2 = head2;
//建立链表A
LNode *node1 = (LNode *)malloc(sizeof(LNode));
node1->data = 1;
LNode *node2 = (LNode *)malloc(sizeof(LNode));
node2->data = 2;
LNode *node3 = (LNode *)malloc(sizeof(LNode));
node3->data = 3;
LNode *node4 = (LNode *)malloc(sizeof(LNode));
node4->data = 4;
LNode *node5 = (LNode *)malloc(sizeof(LNode));
node5->data = 5;
head1->next = node1;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = NULL;
//建立链表B
LNode *node6 = (LNode *)malloc(sizeof(LNode));
node6->data = 6;
LNode *node7 = (LNode *)malloc(sizeof(LNode));
node7->data = 7;
LNode *node8 = (LNode *)malloc(sizeof(LNode));
node8->data = 8;
LNode *node9 = (LNode *)malloc(sizeof(LNode));
node9->data = 9;
LNode *node10 = (LNode *)malloc(sizeof(LNode));
node10->data = 10;
head2->next = node6;
node6->next = node7;
node7->next = node8;
node8->next = node9;
node9->next = node10;
node10->next = node3;
r1 = head1->next, r2 = head2->next;
//遍历链表
while (r1 != NULL)
{
printf("%d ", r1->data);
r1 = r1->next;
}
printf("\n");
// 遍历链表
while (r2 != NULL)
{
printf("%d ", r2->data);
r2 = r2->next;
}
printf("\n");
LNode *node = search_Common(head1, head2);
printf("%d\n", node->data);
system("pause");
}
4、删除操作:
Ⅰ、Demo 1:递归删除x
1)描述:
- 设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点
2)算法思想:
- 定义一个current,pre,pre一直指向current
- 结束条件:如果到了链尾,则return
- 递归主体:如果找到x,则通过pre指针删除current结点,
- 并将pre结点传递给下一层函数
void delete_x_recursion(LNode *list, int x)
{
if (list == NULL)
{
return;
}
else
{
LNode *current, *pre;
pre = list;
current = pre->next;
if (current != NULL && current->data == x)
{
pre->next = current->next;
free(current);
return delete_x_recursion(pre, x);
}else{
return delete_x_recursion(current, x);
}
}
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *node1 = (LNode *)malloc(sizeof(LNode));
node1 -> data = 0;
r = node1;
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 1;
r->next = node;
r = node;
}
}
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 4;
r->next = node;
r = node;
}
}
for (i = 1; i < 4; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 9;
r->next = node;
r = node;
}
}
r->next = NULL;
r = node1;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//删除值为x结点
delete_x_recursion(node1, 4);
r = node1;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
system("pause");
}
Ⅱ、Demo 2:删除所有x
1)描述:
- 在带头结点的单链表L中,删除所有值为x的结点,并释放空间,假设值为x的结点不唯一
2)算法思想:
- 定义一个current,pre,pre一直指向current
- 删除结点时,通过pre来删除current结点
void delete_x_NO_RECUR(LNode *head, int x)
{
if (head == NULL)
{
return;
}
LNode *pre = head,*current = pre->next,*q;
while (current != NULL)
{
if (current->data == x)
{
q = current;
current = current->next;
pre->next = current;
free(q);
}
else
{
pre = current;
current = current->next;
}
}
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *head = (LNode *)malloc(sizeof(LNode));
r = head;
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 1;
r->next = node;
r = node;
}
}
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 4;
r->next = node;
r = node;
}
}
for (i = 1; i < 4; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 9;
r->next = node;
r = node;
}
}
r->next = NULL;
r = head -> next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//删除值为x结点
delete_x_NO_RECUR(head, 4);
r = head -> next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
system("pause");
}
Ⅲ、Demo 3:删除最小值
1)描述:
- 在带头结点的单链表L中,删除一个最小值结点的高效算法(假设最小值结点唯一)
2)算法思想:
- 定义一个current,pre,pre一直指向current
- 定义一个min,minpre,minpre一直指向min
- 删除最小结点时,通过minpre来删除min结点
void delete_min(LNode *head)
{
if (head == NULL)
{
return;
}
LNode *pre = head, *current = pre->next;
LNode *minPre = pre, *min = current;
//找最小值
while (current != NULL)
{
if (current->data < min->data)
{
minPre = pre;
min = current;
}
pre = current;
current = current->next;
}
minPre -> next = min->next;
free(min);
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *head = (LNode *)malloc(sizeof(LNode));
r = head;
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 1;
r->next = node;
r = node;
}
}
for (i = 1; i < 2; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = -1;
r->next = node;
r = node;
}
}
for (i = 1; i < 4; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 9;
r->next = node;
r = node;
}
}
r->next = NULL;
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//删除值为x结点
delete_min(head);
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
system("pause");
}
5、插入操作(头插/尾插)
Ⅰ、Demo 1:就地逆置
1)描述:
- 将带头结点的单链表就地逆置,辅助空间为O(1)
2)算法思想:
- 初始时头指针和第一个结点作为一个整体
- 定义一个current,r指针,将该结点通过头插法插入
void reverse_List(LNode *head)
{
if (head == NULL)
{
return;
}
//若只有一个结点,无需逆置
if(head -> next == NULL || head -> next -> next == NULL){
return;
}
LNode *current = head -> next -> next; //current初始时指向第二个结点
LNode *r; //current的下一个结点
head->next -> next = NULL;
while (current != NULL)
{
r = current -> next;
current->next = head->next;
head->next = current;
current = r;
}
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *head = (LNode *)malloc(sizeof(LNode));
r = head;
for (i = 1; i < 3; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 1;
r->next = node;
r = node;
}
}
for (i = 1; i < 2; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = -1;
r->next = node;
r = node;
}
}
for (i = 1; i < 4; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = 9;
r->next = node;
r = node;
}
}
r->next = NULL;
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//删除值为x结点
reverse_List(head);
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
system("pause");
}
Ⅱ、Demo 2:元素递增
1)描述:
- 有一个带头结点的单链表L,设计一个算法使其元素递增有序
2)算法思想:
- 使用头插法实现插入排序,具体步骤如下
- 初始时头节点和第一个结点构成有序序列A,其余部分为无序序列B
- 定义一个index,每次将B中的第一个结点插入到A中的index位置
- 直至遍历完B中所有结点则结束
void InsertSort(LNode *head)
{
if (head == NULL)
{
return;
}
//若只有一个结点,无需排序
if (head->next == NULL || head->next->next == NULL)
{
return;
}
LNode *pre = head -> next; //current的前一个结点,及A的尾结点
LNode *current = head->next->next; //current初始时指向第二个结点
LNode *r; //current的下一个结点
pre ->next = NULL;
LNode *index; //插入的位置
while (current != NULL)
{
r = current->next;
index = head;
//B的第一个结点比A尾结点小,遍历A
if(current -> data < pre -> data){
while(index != NULL){
if(current -> data < index -> next -> data){
break;
}
index = index -> next;
}
current -> next = index -> next;
index -> next = current;
}
//B的第一个结点比A尾结点大,不用遍历A,直接将B接到A尾部
else{
pre -> next = current;
current -> next = NULL;
}
pre = current; //注:不要忘了三个指针的同步
current = r;
}
}
int main()
{
int i;
LNode *r = NULL; //指向链表队尾的指针
LNode *head = (LNode *)malloc(sizeof(LNode));
r = head;
// for (i = 10; i > 0; --i)
// {
// LNode *node = (LNode *)malloc(sizeof(LNode));
// if (node != NULL)
// {
// node->data = i;
// r->next = node;
// r = node;
// }
// }
for (i = 1; i <= 10; ++i)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
if (node != NULL)
{
node->data = i;
r->next = node;
r = node;
}
}
r->next = NULL;
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
//删除值为x结点
InsertSort(head);
r = head->next;
// 遍历链表
while (r != NULL)
{
printf("%d ", r->data);
r = r->next;
}
printf("\n");
system("pause");
}
Ⅲ、Demo 3:两链表交集
1)描述:
- 已知两个链表A,B分别为两个集合,其元素递增排列,编制函数
- 求A与B的交集,并存于A链表中
2)算法思想:
- 定义指针p,q,初始时分别指向A,B的表头第一个结点
- p,q先比较值的大小,小的结点指针先移动,大的结点指针固定
- 若p,q在比较过程中值相等,则头插法插入A中
- 若有一个链表到头了,则结束
LNode *getUnion(LNode *headA, LNode *headB)
{
if (headA == NULL || headB == NULL)
{
return NULL;
}
if (headA->next == NULL || headB->next == NULL)
{
return NULL;
}
LNode *p = headA->next, *q = headB->next;
LNode *r = q->next; //q的后一个结点,方便q插入到A中
LNode *u; //用来删除A或B中移动后的结点
headA->next = NULL;
free(headB);
while (p != NULL && q != NULL)
{
//B结点头插法到A
if (p->data == q->data)
{
q->next = headA->next;
headA->next = q;
q = r;
//这里q和r要保持同步!!!
if (r != NULL)
{
r = r->next;
}
}
//A结点小,p移动
else if (p->data < q-> data)
{
u = p;
p = p->next;
free(u);
}
//B结点小,q移动
else
{
u = q;
q = r;
if (r != NULL)
{
r = r->next;
}
free(u);
}
}
return headA;
}
int main()
{
int i;
LNode *r1 = NULL, *r2 = NULL; //指向链表队尾的指针
LNode *head1 = (LNode *)malloc(sizeof(LNode));
LNode *head2 = (LNode *)malloc(sizeof(LNode));
r1 = head1, r2 = head2;
//建立链表A
LNode *node1 = (LNode *)malloc(sizeof(LNode));
node1->data = 1;
LNode *node2 = (LNode *)malloc(sizeof(LNode));
node2->data = 2;
LNode *node3 = (LNode *)malloc(sizeof(LNode));
node3->data = 4;
LNode *node4 = (LNode *)malloc(sizeof(LNode));
node4->data = 6;
LNode *node5 = (LNode *)malloc(sizeof(LNode));
node5->data = 9;
head1->next = node1;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = NULL;
//建立链表B
LNode *node6 = (LNode *)malloc(sizeof(LNode));
node6->data = 6;
LNode *node7 = (LNode *)malloc(sizeof(LNode));
node7->data = 7;
LNode *node8 = (LNode *)malloc(sizeof(LNode));
node8->data = 8;
LNode *node9 = (LNode *)malloc(sizeof(LNode));
node9->data = 9;
LNode *node10 = (LNode *)malloc(sizeof(LNode));
node10->data = 10;
head2->next = node6;
node6->next = node7;
node7->next = node8;
node8->next = node9;
node9->next = node10;
node10->next = NULL;
r1 = head1->next, r2 = head2->next;
//遍历链表
while (r1 != NULL)
{
printf("%d ", r1->data);
r1 = r1->next;
}
printf("\n");
// 遍历链表
while (r2 != NULL)
{
printf("%d ", r2->data);
r2 = r2->next;
}
printf("\n");
r1 = getUnion(head1, head2);
// 遍历链表
while (r1 -> next!= NULL)
{
r1 = r1->next;
printf("%d ", r1->data);
}
printf("\n");
system("pause");
}