单链表的插入删除以及逆转
一、背景说明
参加了某团购网站2014年的校招笔试,里面考到了一道单链表反转的题目。要求每隔k个元素反转一次。
如k=1时,链表1,2,3,4,5,6,7,8反转后为1,2,3,4,5,6,7,8
k=3时,链表1,2,3,4,5,6,7,8反转后为3,2,1,6,5,4,8,7
当时不会做,于是回来重新复习了下链表并实现了以下操作:
1)创建头结点
注意这里要把头结点L返回,因为传入的形参是头结点指针,给头结点分配空间后指针地址变化了。所以需要返回指针L才行。
(这里改变的不是指针指向的内容,而是指针的地址)
2)在位置i插入元素
找到要插入的位置i的前一个元素p。
创建一个新的结点s,s的后缀指针指向p的下一个。
p的后缀指针指向s。
3)删除位置i的元素
找到要删除的位置i的前一个元素p。
r为p的下一个元素,即要删除的元素。
p的后缀指针指向r的下一个元素。
释放r。
4)单链表反转
提示:假设有三个元素pPrev,pNode,pNext。要想反转pNode,pPrev,则把pNode的下一个指向pPrev就可以了。
但是此时链表就断开了,所以还需要记录下pNext的位置。不断循环直到链表尾(pNode为空)即可。
5)单链表每隔K个反转
提示:计算链表长度,除以反转间隔k得出要反转的趟数。参照4的方法反转即可。
二、代码实现
#include <stdio.h> #include <stdlib.h> /* 定义一个结构体 */ typedef struct Node{ int data; struct Node *next; }Node,*ListNode; /* 创建一个头结点 */ Node* initial(Node *L){ L=(Node *)malloc(sizeof(Node)); L->next=NULL; return L; } /* 在位置i插入一个元素x 初始化列表是i可置1,即采用头插法添加元素。 */ int Insert(Node* head,int x,int i){ ListNode p; p=head; int j=1; while(p->next&&j<i){//找第i-1个元素p p=p->next; j++; } if(p==NULL || j>i){ printf("insert error"); return -1; } Node* s=(Node *)malloc(sizeof(Node)); s->data = x; s->next = p->next; p->next = s; return 0; } /* 删除第i个元素 */ int Delete(ListNode head,int i){ Node *p,*r; p=head; int j=1; while(p->next&&j<i){//找第i-1个元素p p=p->next; ++j; } if(p==NULL || p->next==NULL || j>i){ printf("delete error"); return -1; } r=p->next; p->next = r->next; free(r); return 0; } /* 单链表反转 */ int Reverse(Node *head){ Node* revHead = NULL; Node* pNode = head->next;//初始为第一个元素 Node* pPrev = NULL;//当前元素的前缀元素指针 Node* pNext = NULL;//当前元素的后缀元素指针 while(pNode!=NULL){ pNext = pNode->next;//保存当前元素的后缀元素 if(pNext==NULL) revHead=pNode; pNode->next=pPrev;//当前元素的下一个指向前缀元素 pPrev=pNode;//前缀元素指向当前元素 pNode=pNext;//当前元素指向下一个要逆转的元素,即后缀元素 } head->next = revHead; return 0; } /* 单链表每隔K个元素反转 如k=3时1,2,3,4,5,6,7,8则反转为3,2,1,6,5,4,8,7 */ int NumReverse(Node *head,int k){ Node* lastTail = head; //上一组列表的表尾 Node* pNode = head->next; //当前元素 Node* groupTail = NULL; //当前组的表尾 Node* pPrev = NULL;//当前元素的前缀元素 Node* pNext = NULL;//当前元素的后缀元素 int count =0; //已反转的趟数 int length =0;//要反转的趟数 while(pNode!=NULL){//计算链表长度 length++; pNode = pNode->next; } pNode = head->next; length=length/k;//要逆转的趟数 while(pNode!=NULL && count<=length){//逆转length躺 pPrev = NULL; pNext = NULL; groupTail = pNode; int i=0; while(pNode!=NULL && i<k){//如果下一个元素还有且当前趟已逆转个数i<k则继续反转 pNext = pNode->next; pNode->next=pPrev; pPrev=pNode; pNode=pNext; i++; } lastTail->next = pPrev; //上一组的链表表尾指向当前趟的第一个元素 lastTail = groupTail; //修改上一组的链表表尾为当前组的链表表尾 groupTail->next = pNode; //当前组的链表表尾元素的后缀指针指向下一组的开始元素 count++; } return 0; } /* 打印链表 */ void Print(Node* head){ Node* p; p=head; while(p->next){ p=p->next; printf("%d ",p->data); } printf("\n"); } int main(){ Node* head; head=initial(head); printf("初始化的链表:\n"); for(int i=1;i<=20;i++){ Insert(head,i,1); } Print(head); printf("反转后的链表:\n"); Reverse(head); Print(head); printf("按间隔k=4反转后的链表:\n"); NumReverse(head,4); Print(head); printf("删除第2-11的元素后的链表:\n"); for(int i=1;i<=10;i++){ Delete(head,2); }; Print(head); return 0; }
三、运行结果
注:代码在Ubuntu下用GCC/G++编译运行均通过