剑指offer(C语言)51-60

51 数组中的逆序对(暂略)

题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6)(7,5)(7,4)(6,4)和(5,4)
思路:没想明白

52 两个链表的第一个公共节点

题目:输入两个链表,找出它们的第一个公共节点。
思路:暴力法,遍历A链表,从第一个节点开始,每个节点都去B链表中遍历寻找有没有相同的节点,时间复杂度为O(N2)
优化一:注意,这里的节点相同,不只是节点中的数值相同,节点的next指向的地址也要相同,这也就是说只会是下面的Y型,不会是X型。所以从尾开始找,第一个不一样的下一个节点就是第一个公共节点了。但是链表只有从前向后的指针,没有从后向前的,所以可以加两个栈,先从头到后遍历一遍,把两个链表每个节点分别放在栈里面。然后不断出栈,比较。
优化二:如果不加入栈也可以,因为最后几个节点是公共的,所以不同的都在前面的节点里面。先比较两个链表的长度,然后从长的链表中先走长度差步,之后再和优化一一样,不断比较就好了。就向下图里面的,上面的离岸边比下面的长1个节点,就让他先走一步,走到2,然后开始比较。

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2

typedef struct ListNode
{
	int data;
	struct ListNode* next;
}listNode;

void PrintList(listNode* head)
{
	listNode* p = head;
	while(p!=NULL)
	{
		printf("%d ",p->data);
		p = p->next;
	}
}

listNode* CreateListNode(int data)
{
	listNode* newListNode = (listNode*)malloc(sizeof(listNode));
	if(!newListNode)return false;
	newListNode->data = data;
	newListNode->next = NULL;
	return newListNode;
}

void ConnectListNodes(listNode* node1,listNode* node2)
{
	node1->next = node2;
}

listNode* FindFirstCommonNode(listNode* pHead1,listNode* pHead2)
{
	listNode* p;
	listNode* shortList = pHead1;
	listNode* longList = pHead2;
	int length1 = 0;
	int length2 = 0;
	int lengthDif = 0;
	p = pHead1;
	while(p!=NULL)
	{
		length1++;
		p = p->next;
	}
	p = pHead2;
	while(p!=NULL)
	{
		length2++;
		p = p->next;
	}
	lengthDif = length2 - length1;
	if(lengthDif<0)
	{
		shortList = pHead2;
		longList = pHead1;
		lengthDif = -lengthDif;
	}
	while(lengthDif!=0)
	{
		longList = longList->next;
		lengthDif--;
	}
	while(shortList!=NULL && longList!=NULL)
	{
		if(shortList == longList)break;
		shortList = shortList->next;
		longList = longList->next;
	}

	return shortList;

}
void main()
{
	int k=1;
	listNode* pNode1 = CreateListNode(1);
    listNode* pNode2 = CreateListNode(2);
    listNode* pNode3 = CreateListNode(3);
    listNode* pNode4 = CreateListNode(4);
    listNode* pNode5 = CreateListNode(5);
	listNode* pNode6 = CreateListNode(6);
	listNode* pNode7 = CreateListNode(7);

	ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode6);
    ConnectListNodes(pNode6, pNode7);

	ConnectListNodes(pNode4, pNode5);
	ConnectListNodes(pNode5, pNode6);

	printf("%d",FindFirstCommonNode(pNode1,pNode4)->data);

}

53-1 数字在排序数组中出现的次数

题目:数字在排序数组中出现的次数。例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4
思路
暴力法,从头到尾查,时间复杂度是O(N).
也可以使用二分法找到3,然后向前向后查,可以省点时间,但还是O(N)
优化:使用二分法分别查第一个3和最后一个3,然后两个值做差就好了。只不过这个二分法要做点特殊处理,在找头的时候,还要保证找到是3的那个数的前一个不是3,否则在左半区域继续找;在找尾的时候,要保证找到是3的那个数的后一个不是3,否则在后半区域继续找。两个二分查找的时间复杂度都是O(logN),所以总的时间复杂度也是O(log(N))

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2

int GetFirstK(int* data,int length,int k,int start,int end)
{
	int middle = (start+end)>>1;
	if(start>end)return NULL;
	if(data[middle]==k)
	{
		if(middle==0||(middle>0 && data[middle-1]!=k))
			return middle;
		else
			return GetFirstK(data,length,k,start,middle-1);
	}
	else if(data[middle]>k)
		return GetFirstK(data,length,k,start,middle-1);
	else
		return GetFirstK(data,length,k,middle+1,end);
}

int GetLastK(int* data,int length,int k,int start,int end)
{
	int middle = (start+end)>>1;
	if(start>end)return NULL;
	if(data[middle]==k)
	{
		if(middle==length-1||(middle<length-1 && data[middle+1]!=k))
			return middle;
		else
			return GetLastK(data,length,k,middle+1,end);
	}
	else if(data[middle]>k)
		return GetLastK(data,length,k,start,middle-1);
	else
		return GetLastK(data,length,k,middle+1,end);
}

int GetNumberOfK(int* data,int length,int k)
{
	int first,end;
	if(data==NULL || length<0)return NULL;
	first = GetFirstK(data,length,k,0,length-1);
	end = GetLastK(data,length,k,0,length-1);
	return end-first+1;
}
void main()
{
	int data[] = {1,2,3,3,3,3,4,5};
	int number;
	number = GetNumberOfK(data,sizeof(data)/sizeof(int),3);
	printf("%d",number);
}

53-2 0-n-1中缺失的数字

题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内,在范围0-n-1内的n个数字有且只有一个数字不在该数组内,请找出这个数字
思路:这个数组的特点就是如果没有缺失数字,应该下标和数组里面的内容相等,这时候缺的是最后一个数字。否则会从某一个位置开始,下标比该数组中的数小。所以这道题就变成了,寻找第一个下标和数不对应的数。使用二分法,很好实现。
注意:特殊情况就是缺失的是最后一个数的时候,这种情况下应该是right不变,left不断向right靠,最后比right大的时候就结束循环了,这时候返回最大的那个数就好了,不然就是错了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2

int GetMissingNumber(int* numbers,int length)
{
	int left = 0;
	int right = length-1;
	int middle = left+right>>1;
	if(numbers==NULL || length<=0)return NULL;
	while(left<=right)
	{
		if(numbers[middle]==middle)
			left = middle+1;
		else 
		{
			if(middle==0 || numbers[middle-1]==middle-1)
				return middle;
			else
				right = middle-1;
		}
		middle = left+right>>1;
	}
	if(left==length)return length;
	return false;
}
void main()
{
	int data[] = { 0 };
	int number;
	number = GetMissingNumber(data,sizeof(data)/sizeof(int));
	printf("%d",number);
}

53-3 数组中数值和下标相等的元素

题目:假设一个单调递增的数组里的每个元素都是整数并且是唯一的。请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。例如,在数组{-3,-1,1,3,5}中,数字3和他的下标相等。
思路:还是二分法,不谈了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

int GetNumberSameAsIndex(int* numbers,int length)
{
	int left = 0;
	int right = length-1;
	int middle = left+right>>1;
	if(numbers==NULL || length<=0)return false;
	while(left<=right)
	{
		if(numbers[middle]==middle)return numbers[middle];
		else if(numbers[middle]<middle)
			left = middle+1;
		else
			right = middle-1;
		middle = left+right>>1;
	}
	return false;
}
void main()
{
	int data[] = { -3, -1, 1, 3, 5 }; 
	int number;
	number = GetNumberSameAsIndex(data,sizeof(data)/sizeof(int));
	printf("%d",number);
}

54 二叉搜索树的第k大节点

题目:给定一棵二叉搜索树,请找出其中第k大的节点。例如,在图6.1中的二叉搜索树里,按节点数值大小顺序,第三大节点的值是4
在这里插入图片描述思路:这就是链表中序遍历的演变,但是只要一跟递归联系上就有点麻烦。先把中序遍历递归的框架写出来,然后在递归左子树和递归右子树之间加东西。因为是第k个节点,所以需要传入k,所以要再写个传入指针的函数。然后初始化一个指针,当这个指针指向NULL的时候说明还没找到,这时候去判断k,如果k等于1,就返回当前节点,如果不等于1继续返回NULL,然后把k-1.
注意:在右子树的递归条件上要加上限制,因为如果之前k已经满足条件了,那就会一直返回,就错了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

typedef struct BinaryTreeNode
{
	int value;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BinaryTreeNode;

BinaryTreeNode* CreateBinaryTreeNode(double value)

{
	BinaryTreeNode* pNode = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	pNode->value = value;
	pNode->left = NULL;
	pNode->right = NULL;
	return pNode;
}
void ConnectTreeNodes(BinaryTreeNode* parent,BinaryTreeNode* left,BinaryTreeNode* right)
{
	if(parent!=NULL)
	{
		parent->left = left;
		parent->right = right;
	}
}
void DestroyTree(BinaryTreeNode** pRoot)
{
	BinaryTreeNode* left = NULL;
	BinaryTreeNode* right = NULL;
	if(*pRoot!=NULL)
	{
		left = (*pRoot)->left;
		right = (*pRoot)->right;
		free(*pRoot);
		pRoot = NULL;
		DestroyTree(&left);
		DestroyTree(&right);
	}
}

BinaryTreeNode* KthNodeCore(BinaryTreeNode* pRoot,unsigned int* k)
{
	BinaryTreeNode* target = NULL;
	if(pRoot->left!=NULL)target = KthNodeCore(pRoot->left,k);
	if(target==NULL)
	{
		if(*k==1)target = pRoot;
		else (*k)--;
	}
	if(pRoot->right!=NULL && target == NULL)target = KthNodeCore(pRoot->right,k);
	return target;
}
BinaryTreeNode* KthNode(BinaryTreeNode* pRoot,unsigned int k)
{
	if(pRoot == NULL || k==0)return NULL;
	return KthNodeCore(pRoot,&k);
}
void main()
{
	BinaryTreeNode* node;

	BinaryTreeNode* node5 = CreateBinaryTreeNode(5);
	BinaryTreeNode* node3 = CreateBinaryTreeNode(3);
	BinaryTreeNode* node7 = CreateBinaryTreeNode(7);
	BinaryTreeNode* node2 = CreateBinaryTreeNode(2);
	BinaryTreeNode* node4 = CreateBinaryTreeNode(4);
	BinaryTreeNode* node6 = CreateBinaryTreeNode(6);
	BinaryTreeNode* node8 = CreateBinaryTreeNode(8);

	ConnectTreeNodes(node5,node3,node7);
	ConnectTreeNodes(node3,node2,node4);
	ConnectTreeNodes(node7,node6,node8);

	node =  KthNode(node5,3);
	printf("%d",node->value);
}

55-1 二叉树的深度

题目:输入一课二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根节点和叶节点)形成树的一条路径,最长路径的长度为树的深度。
思路:深度就是左右子树中比较深的深度加一,递归就行了。从叶子节点开始往回递归,叶子节点的深度为1.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

typedef struct BinaryTreeNode
{
	int value;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BinaryTreeNode;

BinaryTreeNode* CreateBinaryTreeNode(double value)

{
	BinaryTreeNode* pNode = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	pNode->value = value;
	pNode->left = NULL;
	pNode->right = NULL;
	return pNode;
}
void ConnectTreeNodes(BinaryTreeNode* parent,BinaryTreeNode* left,BinaryTreeNode* right)
{
	if(parent!=NULL)
	{
		parent->left = left;
		parent->right = right;
	}
}
void DestroyTree(BinaryTreeNode** pRoot)
{
	BinaryTreeNode* left = NULL;
	BinaryTreeNode* right = NULL;
	if(*pRoot!=NULL)
	{
		left = (*pRoot)->left;
		right = (*pRoot)->right;
		free(*pRoot);
		pRoot = NULL;
		DestroyTree(&left);
		DestroyTree(&right);
	}
}

BinaryTreeNode* KthNodeCore(BinaryTreeNode* pRoot,unsigned int* k)
{
	BinaryTreeNode* temp = NULL;
	if(pRoot->left!=NULL)temp = KthNodeCore(pRoot->left,k);
	if(temp==NULL)
	{
		if(*k!=1) (*k)--;
		else temp = pRoot;
	}
	if(pRoot->right!=NULL && temp==NULL)temp = KthNodeCore(pRoot->right,k);
	return temp;
}
BinaryTreeNode* KthNode(BinaryTreeNode* pRoot,unsigned int k)
{
	if(pRoot == NULL || k==0)return NULL;
	return KthNodeCore(pRoot,&k);
}

int TreeDepth(BinaryTreeNode* pRoot)
{
	int nLeft =0;
	int nRight = 0;
	if(pRoot==NULL)return 0;
	nLeft = TreeDepth(pRoot->left);
	nRight = TreeDepth(pRoot->right);
	if(nLeft>nRight)return nLeft+1;
	else return nRight+1;
}
void main()
{
	BinaryTreeNode* node;

	BinaryTreeNode* node5 = CreateBinaryTreeNode(5);
	BinaryTreeNode* node3 = CreateBinaryTreeNode(3);
	BinaryTreeNode* node7 = CreateBinaryTreeNode(7);
	BinaryTreeNode* node2 = CreateBinaryTreeNode(2);
	BinaryTreeNode* node4 = CreateBinaryTreeNode(4);
	BinaryTreeNode* node6 = CreateBinaryTreeNode(6);
	BinaryTreeNode* node8 = CreateBinaryTreeNode(8);

	ConnectTreeNodes(node5,node3,node7);
	ConnectTreeNodes(node3,node2,node4);
	ConnectTreeNodes(node7,node6,node8);

	printf("%d",TreeDepth(node5));
}

55-2 平衡二叉树

题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1.那么他就是一颗平衡二叉树。例如,图中的二叉树就是一颗平衡二叉树。

在这里插入图片描述
思路:可以根据上面求深度的程序,从根节点开始,比较左右子树深度,如果相差不大于一就证明这个节点是平衡的,继续判断他的左右子树是不是平衡的,一直递归下去,到叶子节点结束。这种思路的缺点是重复计算深度。因为是从根节点开始的。
优化:对于上述情况,我们可以把深度作为函数成员传递,当指针指向NULL时把depth赋值0,之后先判断左子树和右子树是不是都是平衡树,判断成功后再判断左子树和右子树的深度是不是相差一,又判断成功后再将当前节点的深度更改为左右子树更大的那个深度加一,然后返回true,否则返回false

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

typedef struct BinaryTreeNode
{
	int value;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BinaryTreeNode;

BinaryTreeNode* CreateBinaryTreeNode(double value)

{
	BinaryTreeNode* pNode = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	pNode->value = value;
	pNode->left = NULL;
	pNode->right = NULL;
	return pNode;
}
void ConnectTreeNodes(BinaryTreeNode* parent,BinaryTreeNode* left,BinaryTreeNode* right)
{
	if(parent!=NULL)
	{
		parent->left = left;
		parent->right = right;
	}
}
void DestroyTree(BinaryTreeNode** pRoot)
{
	BinaryTreeNode* left = NULL;
	BinaryTreeNode* right = NULL;
	if(*pRoot!=NULL)
	{
		left = (*pRoot)->left;
		right = (*pRoot)->right;
		free(*pRoot);
		pRoot = NULL;
		DestroyTree(&left);
		DestroyTree(&right);
	}
}
int TreeDepth(BinaryTreeNode* pRoot)
{
	int left,right;
	if(pRoot==NULL)return 0;
	left = TreeDepth(pRoot->left);
	right = TreeDepth(pRoot->right);
	return left>right?left+1:right+1;
}


bool IsBalancedCore(BinaryTreeNode* pRoot,int* depth)
{
	int left,right;
	int diff;
	if(pRoot == NULL)
	{
		*depth = 0;
		return true;
	}
	if(IsBalancedCore(pRoot->left,&left) && IsBalancedCore(pRoot->right,&right))
	{
		diff = left-right;
		if(diff<=1 && diff>=-1)
		{
			*depth = 1+(left>right?left:right);
			return true;
		}
	}
	return false;
}
bool IsBalanced(BinaryTreeNode* pRoot)
{
	int depth=0;
	if(pRoot==NULL)return true;
	return IsBalancedCore(pRoot,&depth);
}
bool IsBalanced2(BinaryTreeNode* pRoot)
{
	int left,right;
	if(pRoot==NULL)return true;
	left = TreeDepth(pRoot->left);
	right = TreeDepth(pRoot->right);
	if(left-right>=-1 && left-right<=1)
		return IsBalanced2(pRoot->left) && IsBalanced2(pRoot->right);
	else 
		return false;
}
void main()
{
	BinaryTreeNode* node;

	BinaryTreeNode* node1 = CreateBinaryTreeNode(1);
	BinaryTreeNode* node2 = CreateBinaryTreeNode(2);
	BinaryTreeNode* node3 = CreateBinaryTreeNode(3);
	BinaryTreeNode* node4 = CreateBinaryTreeNode(4);
	BinaryTreeNode* node5 = CreateBinaryTreeNode(5);
	BinaryTreeNode* node6 = CreateBinaryTreeNode(6);
	BinaryTreeNode* node7 = CreateBinaryTreeNode(7);
	BinaryTreeNode* node8 = CreateBinaryTreeNode(8);

	ConnectTreeNodes(node1,node2,node3);
	ConnectTreeNodes(node2,node4,node5);
	ConnectTreeNodes(node3,NULL,node6);
	ConnectTreeNodes(node5,node7,NULL);
	ConnectTreeNodes(node7,node8,NULL);

	printf("%d",IsBalanced(node1));
	printf("%d",IsBalanced2(node1));
}

56 数组中只出现一次的两个数字

题目:一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(N),空间复杂度是O(1)
思路:先抛开时间复杂度和空间复杂度的要求,可以从头遍历数组,然后找除了他以外的数组,看有没有一样的,时间复杂度是O(N2).也可以引入一个建在哈希表,遍历一次数组然后再从哈希表中找出次数为1的数字,时间复杂度是O(N),空间复杂度为O(N).但是同时满足时间复杂度和空间复杂度要怎么实现呢?
如果只有一个出现一次的数字,可以通过同一数异或自己的特性,从头到尾异或全部,最后剩下的就是只出现一次的那个数字了。
再回到这道题,有两个出现一次的数字,从头到尾异或完之后是这两个只出现过一次的数字的异或值。至少有一个位是1,我们可以通过这个位,把整个数组分成两部分,确保每部分中各有一个出现一次的数字。然后再用异或的方式就可以得到这两个数字了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

// 找到num从右边数起第一个是1的位
unsigned int FindFirstBitIs1(int num)
{
    int indexBit = 0;
    while(((num & 1) == 0))
    {
        num = num >> 1;
        ++indexBit;
    }
    return indexBit;
}

// 判断数字num的第indexBit位是不是1
bool IsBit1(int num, unsigned int indexBit)
{
    return (num>>indexBit & 1);
}

void FindNumsAppearOnce(int data[], int length, int* num1, int* num2)
{
	int i,j;
	int resultExclusiveOR = 0;
	unsigned int indexOf1;
    if(data == NULL || length < 2)
        return;
    for(i = 0; i < length; ++i)
        resultExclusiveOR ^= data[i];
	indexOf1 = FindFirstBitIs1(resultExclusiveOR);
    *num1 = *num2 = 0;
    for(j = 0; j < length; ++j)
    {
        if(IsBit1(data[j], indexOf1))
            *num1 ^= data[j];
        else
            *num2 ^= data[j];
    }
}
void main()
{
	int data[] = { 2, 4, 3, 6, 3, 2, 5, 5 };
	int result1, result2;
    FindNumsAppearOnce(data, sizeof(data)/sizeof(int), &result1, &result2);
	printf("%d %d",result1,result2);
}

57-1 和为S的数字

题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得他们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。例如输入数组{1,2,4,7,11,15}和15,输出4和11.
思路:最直观的做法,每一个数都与其他数相加,看看是否等于S,双重循环,时间复杂度为O(N2)
优化:由于数组已经是有序的,所以可以设置两个指针分别指向头尾,用他俩的和与S比较,如果比S小,就让前面的指针加加,指向一个大点的数;如果比S大,就让后面那个指针减减,指向一个小点的数。如果到最后两个指针相遇还未找到,那就是找不到了,返回失败。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

bool FindNumbersWithSum(int data[],int length,int sum,int* sum1,int*sum2)
{
	int* big;
	int* small;
	if(data==NULL ||length<=0)return false;
	big = &data[length-1];
	small = data;

	while(small<big)
	{
		if(*small+*big<sum)
			small++;
		else if(*small+*big>sum)
			big--;
		else
		{
			*sum1 = *small;
			*sum2 = *big;
			return true;
		}
	}
	return false;
}
void main()
{
	int data[] = {1,2,4,7,11,15};
	int sum1 = 0;
	int sum2 = 0;
	int sum = 15;
    FindNumbersWithSum(data, sizeof(data)/sizeof(int), sum,&sum1, &sum2);
	printf("%d %d",sum1,sum2);
}

57-2 和为S的连续正数序列

题目:输入一个整数S,打印出所有和为S的连续正数序列(至少含有两个数)。例如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以打印出三个连续序列1-5 4-6 7-8
思路:设置两个指针,分别指向1和2,判断他们的和与S的关系。如果小于S,那就让big++,然后和加上新的big;如果大于S,就让small++,和减去原来的small;如果等于S,先打印,之后再big++(small–也可以),更新当前和。因为是连续的,所以当small大于middle之后,姐结束循环(比如15,当small=8就结束)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

void FindContinuous(int sum)
{
	int small = 1;
	int big = 2;
	int currentSum = 3;
	int middle = (sum+1)/2;
	int i;
	while(small<middle)
	{
		if(currentSum<sum)
		{
			big+=1;
			currentSum+=big;
		}
		else if(currentSum>sum)
		{
			currentSum-=small;
			small+=1;
		}
		else
		{
			for(i=small;i<=big;i++)
				printf("%d ",i);
			printf("\n");
			big+=1;
			currentSum+=big;
		}
	}
	return;
}
void main()
{
	FindContinuous(100);
}

58-1 翻转单词顺序

题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串“I am a student.”,则输出“student. a I”
思路:字符串整个倒置的题目很好做,两个指针一个指向头,一个指向尾,然后不断交换,不断向对方靠拢就好了。但是这道题还要求字符的顺序不变,所以还需要以空格为分界点,进行局部交换。
以I am a student为例,第一次整体交换得到tneduts a ma I.然后end指针不断寻找空格,找到后就从start到end再次交换,第一次局部交换后为student a ma I.此时end指向第一个空格,所以交换完再把end++,start=end.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

void Reverse(char* start,char* end)
{
	char temp;
	char* record = end;
	while(start<end)
	{
		temp = *start;
		*start = *end;
		*end = temp;
		start++;
		end--;
	}
}
void ReverseSentence(char* str)
{
	char* start;
	char* end;
	char temp;
	if(str == NULL)return;
	start = str;
	end = str+strlen(str)-1;
	Reverse(start,end);
	start = end = str;
	while(*end!='\0')
	{
		while(*end!=' '&&*end!='\0')
			end++;
		Reverse(start,end-1);
		start = ++end;
	}
	return;
}
void main()
{
	char str[] = "i am a student. ";
	ReverseSentence(str);
	printf("%s",str);
}

58-2 左旋字符串

题目:字符串的左旋操作是把字符串前面的若干字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串“abcdefg”和数字2,该函数将返回左旋两位得到的结果“cdefgab”
思路:我的第一个想法就是先建立一个临时数组,从头数n个,放进临时数组,剩下的从第n+1个开始每个向前移动n个,之后再把临时数组中的放到后面。比如abcdefg,左旋三个,就是先建立一个数组,把abc存放进去,然后defg依次向前移动三位,之后再把abc放到最后面,defgabc.
优化:书上提出一种不用临时数组的方法,其实仔细看会发现,这个左旋的过程就是先把整体旋转,然后把前n个旋转,再把剩下的再旋转。比如abcdefg,第一次旋转是gfedcba,假如左旋三个,就把前三个旋转defgcba,再把剩下的旋转,defgabc.
两种代码都写了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

void Reverse(char* start,char* end)
{
	char temp;
	char* record = end;
	while(start<end)
	{
		temp = *start;
		*start = *end;
		*end = temp;
		start++;
		end--;
	}
}
void LeftRotateString2(char* str,int n)
{
	int length = strlen(str);
	char* start1;
	char* end1;
	char* start2;
	char* end2;
	if(str==NULL||length<=0||n<=0||n>length)return;
	start1 = str;
	end1 = str+n-1;
	start2 = str+n;
	end2 = str+length-1;
	Reverse(start1,end1);
	Reverse(start2,end2);
	Reverse(start1,end2);
}

void LeftRotateString1(char* str,int n)
{
	char *temp = (char*)malloc(sizeof(char)*n);
	int i;
	if(str ==NULL || n<=0)return;
	for(i=0;i<n;i++)
		temp[i] = str[i];
	for(i=0;i<strlen(str)-n;i++)
		str[i] = str[i+n];
	for(;i<strlen(str);i++)
		str[i] = temp[i-(strlen(str)-n)];
	free(temp);
}
void main()
{
	char str1[] = "abcdefg";
	char str2[] = "abcdefg";
	LeftRotateString1(str1,3);
	LeftRotateString2(str2,3);
	printf("%s\n%s",str1,str2);
}

59-1 滑动窗口的最大值(暂略)

题目:给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5}
暂时略过

60 n个骰子的点数

题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
思路:书上的两个方法都太难懂了,看了点别人的文章,自己做了一遍。这是一道动态规划的题,可以建立一个二维数组dp[][],前面的标号表明现在是有几个骰子,后面的标号表明值为多少,数组中的值为值为该数的情况个数。最开始是dp[1][1]到dp[1][6]都是1,因为一个骰子有六种可能,每种可能都是1.下一步开始dp[2][2]到dp[2][12]因为两个骰子最小是2最大是12。然后出现多少次与dp[1]有关,如果dp[2][n]的n-1到n-6有再dp[1]中的,就把那个值加上。用公式表达就是f(n,k)=f(n−1,k−1)+f(n−1,k−2)+f(n−1,k−3)+f(n−1,k−4)+f(n−1,k−5)+f(n−1,k−6)
不断往后循环就好了。
做动态规划的题一般需要以下几个步骤:
1.表示状态
2.找出状态转移方程
3.边界处理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bool unsigned int
#define true 1
#define false -1
#define none 2

void PrintProbability(int num)
{
	int** dp = (int**)malloc(sizeof(int*)*(num+1));
	int i=0;
	int j=0;
	int k=0;
	int sum=0;
	if(num<1)return;
	for(i=0;i<=num;i++)
		dp[i] = (int*)malloc(sizeof(int)*(6*num+1));

	for(i=1;i<=num;i++)
		for(j=0;j<=6*num;j++)
			dp[i][j]=0;
	
	for(i=1;i<=6;i++)
		dp[1][i] = 1;

	
	for(i=2;i<=num;i++)
	{
		for(j=1*i;j<=i*6;j++)
		{
			sum=0;
			for(k=1;k<=6;k++)
			{
				if(j-k>0)
					sum+=dp[i-1][j-k];
			}
			dp[i][j]=sum;
		}
	}
	for(i=num;i<=6*num;i++)
		sum+=dp[num][i];
	for(i=num;i<=6*num;i++)
	{
		printf("%f ",dp[num][i]/(float)sum);
	}
	for(i=0;i<=num;i++)free(dp[i]);
	free(dp);
	printf("\n");
}
void main()
{
	PrintProbability(1);
	PrintProbability(2);
	PrintProbability(3);

}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值