【C语言经典题目练习2】

C语言经典题目练习2


一、二分法查找

一、什么是二分查找法?

对于二分查找法简单的来说就是在排好顺序的一组数据中,不断的进行对半分与我们要查找的元素进行比较。

二、算法思想

假设我们查找的元素Key与数组中间元素进行比较 (left 左边边界、right 右边边界、mid中间元素下标
1)key > arr[mid] ,则在arr数组后一半进行查找,left 变成mid +1
2)key < arr[mid] ,则在arr数组前一半进行查找,right变成mid-1
3)key = arr[mid],则结束运算

三、二分查找实例

现在有10个金币,当中有一个是假的,假金币的重量会比真金币的重要轻一点,现在有一个天平,请问最少需要比较多少次才可以找到这枚假金币?

思路:
1)将10枚金币分为两组,每组各5枚金币在一次称重中我们可以得出假金币的一组
2)将假金币的一组分成2 、2 、1将2 个金币的一组进行比较如果在2个金币的一组则再次比较就找到了假金币,如果在1个金币当中则不需在进行比较
3)在这题中最多需要查找3次,最少2次就可以找到

从上面的思想来看就是二分法的很好体现

二分查找demo测试
在使用二分法的关键点是我们要注意查找元素的边界
一般来说分为两种情况:左闭右闭[left,right] 、左闭右开[left,right)

情况一[left,right]

//情况一:左闭右闭 [left,right] 
//二分查找函数 :查找成功返回查找下标,未找到返回-1 
int Binary_Find(int arr[],int n,int value)
{
	int mid = 0,left = 0,right = n - 1; 
	while(left <= right)  //当左边大于右边结束循环
	{
		mid = (left + right)/2; //中间数
		if(value < arr[mid]) //如果查找的数小于中间值
		{
			right = mid - 1;//改变右边的边界	
		}	
		else if(value > arr[mid]) //如果查找的值大于中间值
		{
			left = mid + 1; //改变左边的边界 
		}
		else if(value == arr[mid])
		{
			//找到了该值 返回下标值 
			return mid;
		}	
	}
	return -1; 	
}
  
  
int main(void)
{
	int i = 0;
	int arr[] = {1,2,3,4,5,6,7,8,9};
	int n = sizeof(arr)/sizeof(int); //数组总个数
	int value = 0; //要查找的变量 
	printf("数组元素为:");
	for(i = 0;i<n;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	while(1)
	{
		printf("please input your num:"); 
		scanf("%d",&value);
		if(Binary_Find(arr,n,value))
		{
			printf("value exit in arr\n");
		 } 
	}
	return 0;	
} 

情况二:[left,right)

//情况二:左闭右开[left,right)
//查找成功返回查找元素下标值,失败返回-1 
int Binary_Find2(int arr[],int n,int value)
{
	int left = 0,right = n,middle = 0;
	while(left < right) //左闭右开注意边界条件不能取等
	{
		middle = (left + right)/2;
		if(value < arr[middle]) //如果目标值小于arr[middle]
		{
			//更新左边区域的right值
			right = middle; 
		}	
		else if(value > arr[middle])
		{
			//更新右边区域的left值
			left = middle + 1; 
		}
		else if(value == arr[middle]) //相等则找到了
		{
			return middle;  //找到了返回下标值 
		 } 
	}	
	return -1; //未找到返回-1 
} 
  
int main(void)
{
	int i = 0;
	int arr[] = {1,2,3,4,5,6,7,8,9};
	int n = sizeof(arr)/sizeof(int); //数组总个数
	int value = 0; //要查找的变量 
	printf("数组元素为:");
	for(i = 0;i<n;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	while(1)
	{
		printf("please input your num:"); 
		scanf("%d",&value);
		if(Binary_Find2(arr,n,value))
		{
			printf("value exit in arr\n");
		}
	}
	return 0;	
} 

在这里插入图片描述

二、删除数组中元素

删除数组中某个元素并返回删除后的数组元素个数
思路:对于删除数组中的元素有多种方式,这里使用的是双指针的方式,快慢指针:快指针记录数组元素的值,慢指针记录数组元素的下标

双指针方式

#include <stdio.h>

//删除数组中某个元素并返回删除后元素的个数
int Delete_Element(int arr[],int n,int target)
{
	int fast = 0,slow = 0; //使用双指针操作
	//fast快指针表示数组元素的值
	//slow慢指针表示数组元素值的下标
	for(fast = 0;fast < n;fast++)
	{
		if(arr[fast] != target) //如果快指针的值不是我们的目标值
		{
			arr[slow++] = arr[fast]; //将该值赋值给新数组 
		 } 
	 } 
	 return slow; //返回总个数 
 } 
int main(void)
{
	int arr[] = {1,4,2,8,6};
	int n = sizeof(arr)/sizeof(int);
	int res = 0,value = 0,i = 0;
	printf("删除前数组元素个数为:%d\n",n);
	printf("数组元素为:");
	for(i = 0;i<n;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\nplease input target value:");
	scanf("%d",&value); 
	res = Delete_Element(arr,n,value);
	printf("\n删除后数组元素个数为:%d\n",res);
	printf("数组元素为:");
	for(i = 0;i<res;i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
 } 

在这里插入图片描述

三、有序数组的平方

将数组元素进行平方后升序排序(包含负数)

#include <stdio.h>

void Order_Array_Pow(int arr[],int n)
{
	int result[sizeof(arr)/sizeof(int)] = {0};  //新数组元素个数 
	int i = 0,j = 0,k = n - 1; //k为新数组的最后一个下标 
	for(i = 0,j = n-1; i <= j;) //循环 
	{
		if((arr[i] * arr[i]) > (arr[j] * arr[j]))
		{
			result[k--] = arr[i]*arr[i];
			i++;
		}
		else
		{
			result[k--] = arr[j]*arr[j];
			j--;
		}
	}
	for(i =0;i<n;i++)
	{
		printf("%d ",result[i]);
	}	
	printf("\n");
}

int main(void)
{
	int arr[] = {-5,3,2,1,4};
	int n = sizeof(arr)/sizeof(int);
	Order_Array_Pow(arr,n);
	return 0;
 } 

在这里插入图片描述

四、判断链表是否为环形链表

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

在这里插入图片描述

解题思路

1、判断该链表是否有环(快慢指针)
2、如何找到环的入口

可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。


struct Node *DetectCycle(struct Node *head)
{
	struct Node *fast = head; //快指针 
	struct Node *slow = head; //慢指针
	//当快指针的值不为空并且下一个值不为空的时候 
	while(NULL != fast && NULL != fast->next) 
	{
		fast = fast->next->next; //快指针一次移动两个节点
		slow = slow->next;       //慢指针一次移动一个节点  
		if(fast == slow)  //如果快慢指针相遇
		{
			struct Node *index1 = fast; //index1为fast此时的索引 
			struct Node *index2 = head;//index2为slow指针索引(从Head开始) 
			while(index1 != index) 
			{
				index1 = index1->next; //索引值后移 
				index2 = index2->next; //索引值后移 
			}
			return index2; //返回环的入口 
		 } 
	} 
	 return NULL;  //无环 
}

五、删除链表倒数第N个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点

在这里插入图片描述

思路

双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了,这里有一个小细节,我们要将快指针移n+1个节点,然后快慢指针在同时移动,这样的话慢指针刚好到我们要删除节点的前一个节点。

假设一共有6个节点,我们要删除倒数第3个也就是我们标记的delete节点,此时先让快指针一定N+1个节点也就是4个节点到delete后一个节点

在这里插入图片描述

此时快慢指针同时移动,当fast指针为空时,slow指针的下一个就是要删除的节点

在这里插入图片描述

//带有头节点 
struct Node *Delete_N_Node(struct Node *head,int n)
{
	struct Node *fast = head; //快指针开始指向头节点 
	struct Node *slow = head; //慢指针开始指向头节点
	while(n-- && fast!= NULL)
	{
		fast = fast->next;
	}
	fast = fast->next; //fast指针在提前走一步,让slow指针指向删除的上一个节点 
	while(NULL != fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	slow->next = slow->next->next;
	return head; 
}

六、实现字符串的逆序(单词逆序输出)

实现思路:首先将整个字符串进行逆序,也就是第一个和最后一个交换,第二个和倒数第二个进行交换,,,,依次这样就得到了字符串的逆序。
在将逆序后的字符串,每个单词再次进行逆序,这里需要进行单词开始和结束的判断

#include <stdio.h>
#include <string.h>


//逆序翻转
void Reverse_String(char *Left,char *Right)
{
	char temp = 0;
	while(Left < Right)
	{
		temp = *Left;
		*Left = *Right;
		*Right = temp;
		Left++;
		Right--;
	}
}


int main(void)
{
	char str[24] = {0};
	int Len = 0;
	printf("please input str:");
	gets(str);
	Len = strlen(str);
	printf("Before Reverse str:%s\r\n",str);
	Reverse_String(str,str+Len-1);
	printf("After Reverse str:%s\r\n",str);
	
	//内部单词再次翻转
	char *start = str;    //指向单词的开头 
	while(*start != '\0')
	{
		char *end = start; //进行指针移动 单词结束判断 
		while(*end != ' ' && *end != '\0')
		{
			end++;
		}
		Reverse_String(start,end-1);
		if(*end != '\0')
		{
			start = end + 1;
		}
		else
		{
			start = end;
		}
	 } 
	 
	printf("After Reverse str:%s\r\n",str);
	return 0;
 } 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小殷学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值