单链表的插入删除以及逆转

一、背景说明

参加了某团购网站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++编译运行均通过

单链表的插入删除以及逆转


 

 

你可能感兴趣的:(单链表)