剑指offer(C语言)31-40

31 栈的压入、弹出序列

题目:输入两个整数序列。第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列{1,2,3,4,5}是某栈的压栈序列,序列{4,5,3,2,1}是该压栈序列对应的一个弹出序列,但{4,3,5,1,2}就不可能是该压栈序列的弹出序列。
思路
先将一下我自己的思路,入栈序列有多少个数就循环多少次,然后每次先入栈一个数,之后循环出栈(只要栈顶和出栈序列相等就可以出栈),成功出栈一次,出栈序列就加一。跳出循环后看出栈序列的地址减出栈序列初始地址是不是等于元素数就可以判断成功和失败。
答案的思路是,进来先判断能不能满足出栈条件,不能出栈就循环进栈,如果没有东西可以进栈了 就循环结束。
两种思路都可以,我也都写了,但是结束循环的条件不太一样,自己写一下感受感受。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20

typedef struct stack
{
	int *top;
	int *base;
	int stackSize;
}stack;
/************栈的辅助代码*******************/
bool Push(stack* stack1,int data)
{
	int* temp;
	//栈满
	if(stack1->top - stack1->base >=stack1->stackSize)
	{
		temp = (int*)realloc(stack1->base,sizeof(int)*(stack1->stackSize+INCREMENT));
		if(temp == NULL)return false;
		stack1->base = temp;
		stack1->top = stack1->base + stack1->stackSize;
		stack1->stackSize+=INCREMENT;
	}
	*stack1->top++ = data;
	return true;
}

int Pop(stack* stack1)
{
	if(stack1->stackSize<=0)
		return 0;
	stack1->stackSize-=1;
	return *--stack1->top;
}

stack* InitStack()
{
	stack* stack1= (stack*)malloc(sizeof(stack)); 
	stack1->base = (int*)malloc(sizeof(int)*INITLENGTH);
	if(stack1->base!=NULL)
	{
		stack1->top = stack1->base;
		stack1->stackSize = INITLENGTH;
		return stack1;
	}
	return NULL;
}
/************************************************/

bool IsPopOrder(const int* pPush, const int* pPop, int nLength)
{
	stack* stack1 = InitStack();
	const int* pPushStart = pPush;
	const int* pPopStart = pPop;
	if(pPush == NULL || pPop == NULL || nLength<=0)
		return false;
	while(pPop-pPopStart<nLength)
	{
		while(*(stack1->top-1)!=*pPop && pPush-pPushStart<nLength)
		{
			Push(stack1,*pPush++);
		}
		if(*(stack1->top-1)==*pPop)
		{
			*pPop++;
			Pop(stack1);
		}
		else
			break;
	}

	if(pPop-pPopStart == nLength )
		return true;
	else 
		return false;
	/*
	stack* stack1 = InitStack();
	const int* pPushStart = pPush;
	const int* pPopStart = pPop;
	if(pPush == NULL || pPop == NULL || nLength<=0)
		return false;
	while((pPush - pPushStart <nLength))
	{
		Push(stack1,*pPush++);
		while(*(stack1->top-1)== *pPop)
		{
			Pop(stack1);
			pPop++;
		}	
	}
	if(pPop-pPopStart==nLength)
		return true;
	else 
		return false;*/
}
void main()
{
	const int nLength = 5;
    int push[] = {1,2,3,4,5};
    int pop[] = {4,5,3,2,1};
	printf("%d",IsPopOrder(push,pop,nLength));

}

32 从上到下打印二叉树

题目一:不分行从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。例如,输入图中的二叉树,则依次打印出8,6,10,5,7,9,11.
在这里插入图片描述思路
这是二叉树的遍历,但是和我们熟悉的前序中序后序遍历不一样,他是每层遍历。捋一下,是怎么一个过程。先判断8不是空,把8打印出来,然后再打印8的左孩子,再打印8的右孩子;再打印8的左孩子的左孩子。。。。。。会发现规律,可以设置一个打印序列,最开始序列中只有8,打印完8加入6和10;打印完6再加入5和7.。。。。由于是FIFO,所以可以用队列的方式。
备注
苦逼的C语言不自带队列函数,所以还要自己写。队列因为是FIFO,所以最好写成环形的,这样不会浪费。但是如果初次分配空间不足之后,如何开辟新的空间,没想好。所以只是自己写了一个不太完善的环形队列先用着。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20


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

typedef struct
{
	BinaryTreeNode** treeNode;
	int head;
	int tail;
	int queueSize;
}seqQueue;
/***************环形队列辅助代码*******************/
seqQueue* InitSeqQueue()
{
	seqQueue* queue = (seqQueue*)malloc(sizeof(seqQueue));
	queue->treeNode = (BinaryTreeNode**)malloc(sizeof(BinaryTreeNode*)*INITLENGTH);
	queue->tail = 0;
	queue->head = 0;
	if(!queue->treeNode)
		return NULL;
	queue->queueSize = INITLENGTH;
	return queue;
}

bool SeqQueuePush(seqQueue* queue,BinaryTreeNode* treeNode)
{
	if(queue->tail % queue->queueSize == queue->head-1)
		return false;
	queue->treeNode[queue->tail] = treeNode;
	queue->tail = (queue->tail+1)%queue->queueSize;
	return true;
}

BinaryTreeNode* SeqQueuePop(seqQueue* queue)
{
	BinaryTreeNode* temp;
	if(queue->tail==queue->head)
		return NULL;
	temp = queue->treeNode[queue->head];
	queue->treeNode[queue->head] = NULL;
	queue->head = (queue->head+1)%queue->queueSize;
	return temp;
}
bool IsEmptySeqQueue(seqQueue* queue)
{
	if(queue->tail==queue->head)
		return true;
	else
		return false;

}
/**************************************************/
/**************二叉树辅助代码*********************/
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);
	}
}

void PrintPreorder(BinaryTreeNode* head)
{
	BinaryTreeNode* temp;
	seqQueue* treeQueue = InitSeqQueue();
	if(head == NULL)
		return;
	SeqQueuePush(treeQueue,head);
	while(!IsEmptySeqQueue(treeQueue))
	{
		temp = SeqQueuePop(treeQueue);
		printf("%d ",temp->value);
		if(temp->left)
			SeqQueuePush(treeQueue,temp->left);
		if(temp->right)
			SeqQueuePush(treeQueue,temp->right);
	}
}
/**************************************************/
void main()
{
	BinaryTreeNode* node1 =  CreateBinaryTreeNode(8);
	BinaryTreeNode* node2 =  CreateBinaryTreeNode(6);
	BinaryTreeNode* node3 =  CreateBinaryTreeNode(10);
	BinaryTreeNode* node4 =  CreateBinaryTreeNode(5);
	BinaryTreeNode* node5 =  CreateBinaryTreeNode(7);
	BinaryTreeNode* node6 =  CreateBinaryTreeNode(9);
	BinaryTreeNode* node7 =  CreateBinaryTreeNode(11);

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

	PrintPreorder(node1);

}

题目2:从上到下按层打印二叉树,同一层的节点按从左到右的舒徐打印,每一层打印到一行
在这里插入图片描述
思路:就是在上一道题的基础上,在每行结束的时候打印一个换行。怎么实现呢。队列中的每个节点都是按顺序添加的,首先只需要一个量thisLineCount,去知道当前行还剩多少个,打印一次,thisLineCount-1,到0的时候就打印换行。
看似解决了,但是还不行,因为不知道下一行有多少,所以还需要一个变量来记录下一行有多少。
8进队列,thisLineCount+1;
8出列,thisLineCount-1;6进队列,nextLineCount+1,10进队列,nextLineCount+1.检查thisLineCount = 0,打印换行,然后thisLineCount = nextLineCount,nextLineCount = 0.一定要想好顺序。

void PrintPreorder(BinaryTreeNode* head)
{
	int thisLineCount = 1;
	int nextLineCount = 0;
	BinaryTreeNode* temp;
	seqQueue* treeQueue = InitSeqQueue();
	if(head == NULL)
		return;
	SeqQueuePush(treeQueue,head);
	while(!IsEmptySeqQueue(treeQueue))
	{
		temp = SeqQueuePop(treeQueue);
		printf("%d ",temp->value);
		thisLineCount--;
		
		if(temp->left)
		{
			SeqQueuePush(treeQueue,temp->left);
			nextLineCount++;
		}
		if(temp->right)
		{
			SeqQueuePush(treeQueue,temp->right);
			nextLineCount++;
		}

		if(!thisLineCount)
		{
			printf("\n");
			thisLineCount = nextLineCount;
			nextLineCount = 0;
		}
	}
}

33 二叉搜索树的后序遍历序列

题目:输入一个整数数组,判断该数组是不是某 二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字 都互不相同。例如,输入数组{5,7,6,9,11,10,8}则返回true.因为这个整数序列是下图中的后序遍历结果。如果输入的数组是{7,4,6,5},则由于没有哪一棵二叉搜索树的后序遍历结果是这个序列,因此返回false.
思路:
先明确二叉搜索树的概念:二叉搜索树就是所有左子树都比根节点小,所有右子树都比根节点大。
再看这道题,后序遍历就是左右根的顺序遍历二叉树,所以输入数组的最后一个值就是该树的根节点,如果是二叉搜索树,应该前面会有一个分界点,前面的都比根小,后面的都比根大。
用语言描述过程:输入数组,数组的最后一个值为根节点,从数组的第一个值开始查找,查找到第一个比根大的值分界,前面为这个树的左子树;之后遍历剩下的,如果所以都比根小则把他判定为右子树,否则直接返回失败。有了左右子树之后,再分别按上述过程递归,最后截止条件是子树中只有1个值(两个也行)。
注意
1.右子树传入长度的时候要记得把根节点减掉,不然死循环了
2.截止条件是该子树长度个数<=2,1,0,都可以。0个1个就不说了,2个也可以因为剩两个的话可以一个是另一个的右子树,也可以另一个是这个的左子树,都可以。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20


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);
	}
}

bool VerifySequenceOfBST(int* sequence,int length)
{
	int root;
	int i;
	int endLeft;
	int statusLeft,statusRight;
	if(sequence == NULL || length <= 0)
		return false;
	root = sequence[length-1];
	for(i=0;i<length-1;i++)
	{
		if(sequence[i]>root)
			break;
	}
	endLeft = i;//记录左子树的个数
	for(;i<length-1;i++)
	{
		if(sequence[i]<root)
			return false;
	}

	if(endLeft<=2)//0或者1或者2
		statusLeft = true;
	else
		statusLeft = VerifySequenceOfBST(sequence,endLeft);

	if(length-1-endLeft<=2)//或者2
		statusRight = true;
	else
		statusRight = VerifySequenceOfBST(&sequence[endLeft+1],length-1-endLeft);
	return statusLeft && statusRight;
}

void main()
{
	//int sequence[] = {5,7,6,9,11,10,8};
	int sequence[]= {7,4,6,5};
	//int sequence[]= {1,2,3,4};
	if(VerifySequenceOfBST(sequence,sizeof(sequence)/sizeof(int)))
		printf("YES");
	else
		printf("NO");
}

34 二叉树中和为某一值的路径

题目:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点一条路径。
思路:先明确路径的概念,路径是从根节点到叶子节点的所有节点的集合。一定是叶子节点。
这道问题是二叉树的遍历问题,只不过遍历要从根节点开始,也就是先序遍历,用递归。而且在遍历的过程中要不断的判断两件事:1.当前路径的和是否等于目标和;2.现在到没到叶子。这个很好实现,叶子的判断只要看有没有孩子就好了,目标和可以在函数传入一个变量,每次路径新加值的时候更新这个变量就好了。
下一个问题:如何打印路径,只有指向孩子的节点,没有指向父母的节点啊,所以需要借助一个队列,把当前路径的所有节点值都入队列,判断成功后从队列头打印到队列尾就可以了。
还有一个问题,如果判断这条路径不行了怎么办?要把这个节点从队列中删掉,也就是后入先出,所以我们可以用栈,到叶子后,是就打印,不是就不打印,之后把这个叶子节点出栈,把现在的和减去这个叶子节点。
所以递归函数要做下面几件事:
1.把节点入栈,currentSum加
2.判断是否是叶子,并且值是否相等,相等就打印
3.如果有左孩子,递归左孩子
4.如果有右孩子,递归右孩子
5.把节点出栈,currentSum减
备注:2页可以放在34的后面,没什么关系。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20


typedef struct BinaryTreeNode
{
	int value;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BinaryTreeNode;
typedef struct stack
{
	int *top;
	int *base;
	int stackSize;
}stack;
/************栈的辅助代码*******************/
bool Push(stack* stack1,int data)
{
	int* temp;
	//栈满
	if(stack1->top - stack1->base >=stack1->stackSize)
	{
		temp = (int*)realloc(stack1->base,sizeof(int)*(stack1->stackSize+INCREMENT));
		if(temp == NULL)return false;
		stack1->base = temp;
		stack1->top = stack1->base + stack1->stackSize;
		stack1->stackSize+=INCREMENT;
	}
	*stack1->top++ = data;
	return true;
}

int Pop(stack* stack1)
{
	if(stack1->stackSize<=0)
		return 0;
	stack1->stackSize-=1;
	return *--stack1->top;
}

stack* InitStack()
{
	stack* stack1= (stack*)malloc(sizeof(stack)); 
	stack1->base = (int*)malloc(sizeof(int)*INITLENGTH);
	if(stack1->base!=NULL)
	{
		stack1->top = stack1->base;
		stack1->stackSize = INITLENGTH;
		return stack1;
	}
	return NULL;
}
/*************************************************/
/**************二叉树辅助代码*********************/
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);
	}
}

void FindPathCore(BinaryTreeNode* pRoot,int expectedSum,int currentSum,stack *stack1)
{
	bool isLeaf = false;
	int *temp = NULL;
	isLeaf = pRoot->left == NULL && pRoot->right == NULL;
	currentSum+=pRoot->value;
	Push(stack1,pRoot->value);

	if(isLeaf && expectedSum == currentSum)
	{
		for(temp = stack1->base;temp<stack1->top;temp++)
			printf("%d ",*temp);
		printf("\n");
	}

	if(pRoot->left)
		FindPathCore(pRoot->left,expectedSum,currentSum,stack1);
	if(pRoot->right)
		FindPathCore(pRoot->right,expectedSum,currentSum,stack1);

	currentSum-=pRoot->value;
	Pop(stack1);
}
void FindPath(BinaryTreeNode* pRoot,int expectedSum)
{
	int currentSum = 0;
	stack *stack1 = InitStack();
	if(pRoot == NULL)
		return;
	FindPathCore(pRoot,expectedSum,currentSum,stack1);
}

void main()
{
	BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNode12 = CreateBinaryTreeNode(12);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode7 = CreateBinaryTreeNode(7);
    ConnectTreeNodes(pNode10, pNode5, pNode12);
    ConnectTreeNodes(pNode5, pNode4, pNode7);

	FindPath(pNode10,19);
}

35 复杂链表的复制

题目:请实现函数ComplexListNode* Clone(ComplexListNode* pHead)复制一个复杂链表。在复杂链表中,每个节点除了有一个next指针还有一个sibling指针指向链表中的任意节点或者NULL。
思路
先说说没看解析之前的想法:
1.先遍历链表,把整条链表复制出来
2.再遍历链表,如果有sibling,就再遍历原链表查出指向第n个,然后去新链表找到有sibling的那个节点,然后指向第n个。这样时间复杂度有点高,O(N2)
解析给的第一个新思路:
在第一次遍历的之后建立一个哈希表,把链表中的节点value都放到哈希表中。找sibling的时候,直接根据值取哈希表里就能找到该节点在哪个位置了。
第二个思路:
这个思路很巧妙,复制的时候不是新复制一个链表,而是在原链表的原节点后面复制出一个。然后找sibling的之后,直接把该节点后面的节点的sibling指向原sibling后面的节点就可以了。然后再把偶数的链表拎出来就是新链表了。一共三步:
1.在原链表上每个节点的后面复制新节点
2.遍历链表,把sibling全部指好
3.遍历链表,把偶数节点拎出来连到一起
每一步写一个函数,思路更清晰。

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

typedef struct ComplexListNode
{
	int value;
	struct ComplexListNode* next;
	struct ComplexListNode* sibling;
}ComplexListNode;

/***********************复杂链表辅助代码************************/
ComplexListNode* CreateNode(int value)
{
	ComplexListNode* node = (ComplexListNode*)malloc(sizeof(ComplexListNode));
	if(node == NULL)return NULL;
	node->next = NULL;
	node->sibling = NULL;
	node->value = value;
	return node;
}

void BuildNodes(ComplexListNode* pNode, ComplexListNode* pNext, ComplexListNode* pSibling)
{
	if(pNode == NULL)return;
	pNode->next = pNext;
	pNode->sibling = pSibling;
}
/******************************************************************/
void CloneNodes(ComplexListNode* pHead)
{
	ComplexListNode* pCloned;
	ComplexListNode* pNode = pHead;
	while(pNode)
	{
		pCloned = (ComplexListNode*)malloc(sizeof(ComplexListNode));
		pCloned->value = pNode->value;
		pCloned->next = pNode->next;
		pCloned->sibling = NULL;
		pNode->next = pCloned;
		pNode = pCloned->next;
	}
}

void ConnectSiblingNodes(ComplexListNode* pHead)
{
	ComplexListNode* pNode = pHead;
	while(pNode!=NULL)
	{
		if(pNode->sibling!=NULL)
		{
			pNode->next->sibling = pNode->sibling->next;
		}
		pNode = pNode->next->next;
	}
}

ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
{
	ComplexListNode* pClonedHead = NULL;
	ComplexListNode* pClonedNode = NULL;
	ComplexListNode* pNode = NULL;
	if(pHead == NULL)return ;
	
	pNode = pHead;
	pClonedHead = pHead->next;
	pClonedNode = pHead->next;

	while(pClonedNode!=NULL && pNode!=NULL)
	{
		if(pClonedNode!=NULL)
		{
			pNode->next = pClonedNode->next;
			pNode = pClonedNode->next;
		}
		if(pNode!=NULL)
		{
			pClonedNode->next = pNode->next;
			pClonedNode =  pNode->next;
		}
	}
	return pClonedHead;
}
ComplexListNode* Clone(ComplexListNode* pHead)
{
	if(pHead == NULL)return NULL;
	CloneNodes(pHead);
	ConnectSiblingNodes(pHead);
	return ReconnectNodes(pHead);
}

void main()
{
	ComplexListNode* pCloneHead = NULL;
	ComplexListNode* pNode1 = CreateNode(1);
    ComplexListNode* pNode2 = CreateNode(2);
    ComplexListNode* pNode3 = CreateNode(3);
    ComplexListNode* pNode4 = CreateNode(4);
    ComplexListNode* pNode5 = CreateNode(5);

    BuildNodes(pNode1, pNode2, pNode3);
    BuildNodes(pNode2, pNode3, pNode5);
    BuildNodes(pNode3, pNode4, NULL);
    BuildNodes(pNode4, pNode5, pNode2);

	pCloneHead = Clone(pNode1);
}

36 二叉搜索树与双向链表

题目:输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。比如输入下图中左边的二叉搜索树,则输出转换以后的排序双向链表。
在这里插入图片描述思路:这道题有点不好想,确实把我绕进去了。第一步很明确,左孩子替代成双向链表中的前向指针,右孩子代替成双向链表中的后向指针,而且也很容易想到这是二叉树的中序遍历,剩下就不知道怎么做了。画画图,推导几步就找到规律了。
在这里插入图片描述
红色指针代表函数传的指针,蓝色指针代表函数递归到哪个节点,绿色指针代表新建立的双向链表节点
中序遍历最开始肯定可以找到最左的子树,也就是双向链表中的最小的一项,啥也不用做。递归函数返回到6,6和4建立双指针。递归再回到8,8和6建立双指针。最后按照右下角的顺序遍历,每次和前面的一项建立双指针就好了。所以这道题就简化成了,在中序遍历的前提下,加一个指针pLastNode ,指向前一个被递归的节点,两个指针间建立双向链表。因为这个指针是不断改指向位置的,所以必须使用二级指针。
最后返回的时候,pLastNode 不断向左寻找,找到头的时候把它返回。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#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);
	}
}
/**********************************************/
void ConvertNode(BinaryTreeNode* pNode,BinaryTreeNode** pLastNode)
{
	if(pNode->left)
		ConvertNode(pNode->left,pLastNode);

	if(*pLastNode)
	{
		pNode->left = *pLastNode;
		(*pLastNode)->right = pNode;
	}
	*pLastNode = pNode;

	if(pNode->right)
		ConvertNode(pNode->right,pLastNode);
}

BinaryTreeNode* Convert(BinaryTreeNode* pRoot)
{
	BinaryTreeNode* pLastNode = NULL;
	if(pRoot == NULL)return;
	ConvertNode(pRoot,&pLastNode);
	while(pLastNode->left)
		pLastNode = pLastNode->left;
	return pLastNode;
}

void main()
{
	BinaryTreeNode* convert = NULL;
	BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
	BinaryTreeNode* pNode6 = CreateBinaryTreeNode(6);
	BinaryTreeNode* pNode14 = CreateBinaryTreeNode(14);
	BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
	BinaryTreeNode* pNode8 = CreateBinaryTreeNode(8);
	BinaryTreeNode* pNode12 = CreateBinaryTreeNode(12);
	BinaryTreeNode* pNode16 = CreateBinaryTreeNode(16);

	ConnectTreeNodes(pNode10,pNode6,pNode14);
	ConnectTreeNodes(pNode6,pNode4,pNode8);
	ConnectTreeNodes(pNode14,pNode12,pNode16);

	convert = Convert(pNode10);
}

37 序列化二叉树

题目:请实现两个函数,分别用来序列化和反序列化二叉树。
思路:可以根据前序遍历的顺序来序列化二叉树,在碰到NULL指针是,用特殊字符$,节点的数值之间用,隔开。
备注:C语言char的递归太麻烦了。因为传入char指针,所以不希望他被更改,应该用一级指针,但是在递归的过程中又需要这个指针指向下一个值,试了好久,最后 通过两个函数实现,第一个函数传入一级指针,然后直接取一级指针的地址作为二级指针函数的递归输入。
这道题能很好地考验对二级指针的掌握,我写的只是满足了基本要求,有很多不完善的地方,实在是心态搞崩了。大家参考一下就好。

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20


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);
	}
}
/*****************************************************/

void DeserializeCore(BinaryTreeNode** pRoot,char** str)
{
	if(**str>='0' &&**str<='9')
	{
		*pRoot = CreateBinaryTreeNode(**str-'0');
		*str+=2;
		DeserializeCore(&(*pRoot)->left,str);
		*str+=2;
		DeserializeCore(&(*pRoot)->right,str);
	}
}
void Deserialize(BinaryTreeNode** pRoot,char* str)
{
	DeserializeCore(pRoot,&str);
}
void SerializeCore(BinaryTreeNode* pRoot,char** str)
{
	if(pRoot == NULL)
	{
		**str = '$';
		(*str)++;
		**str = ',';
		(*str)++;
		return;
	}
	else
	{
		**str = '0'+pRoot->value;
		(*str)++;
		**str = ',';
		(*str)++;
	}
	SerializeCore(pRoot->left,str);
	SerializeCore(pRoot->right,str);
}
void Serialize(BinaryTreeNode* pRoot,char* p)
{
	SerializeCore(pRoot,&p);
}

void main()
{
	int i =0;
	char* str = (char*)malloc(sizeof(char)*INITLENGTH);
	BinaryTreeNode* pRoot;
	BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);
    BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
	BinaryTreeNode* pNode6 = CreateBinaryTreeNode(6);
    ConnectTreeNodes(pNode1, pNode2, pNode3);
    ConnectTreeNodes(pNode2, pNode4, NULL);
	ConnectTreeNodes(pNode3, pNode5, pNode6);
	for(i=0;i<INITLENGTH;i++)
		str[i] = '\0';
	Serialize(pNode1,str);
	Deserialize(&pRoot,str);
}

38 字符串的排列

题目:输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串abc,则打印出:abc,acb,bac,bca,cab和cba。
思路:用人的思维去思考这个打印的过程:第一个字母有abc三种选择,然后确定了第一个之后(比如a),第二个字母有bc两种选择,然后确定了第二个之后(比如b),第三个字母就只有c一个选择了,这时候就可以打印abc了。打印完之后返回,再把第二个字母换成另一个,再往下走,打印出acb。然后再往回回。
这是很明显的递归类问题,设定一个指针指向第一个节点,把这个节点和后面的所有节点都换,换完一次就继续这个指针往后移。当这个指针指向‘\0’的时候说明结束了,打印。由于打印的时候需要头指针,所以传递参数的时候把头指针也传进去。
注意:还是要注意下char[] str 和char* str的区别。像这道题需要改变字符串,就只能用char[] str,用char* str时就会出错。

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

void PermutationCore(char* pStr,char* pBigin)
{
	char* p = pBigin;
	char temp;
	if(*pBigin=='\0')
		printf("%s\n",pStr);
	for(;*p!='\0';p++)
	{
		temp = *pBigin;
		*pBigin = *p;
		*p = temp;
		PermutationCore(pStr,pBigin+1);
		temp = *pBigin;
		*pBigin = *p;
		*p = temp;
	}
}
void Permutation(char* pStr)
{
	if(pStr == NULL)return;
	PermutationCore(pStr,pStr);
}

void main()
{
	char str[]= "abcd";
	Permutation(str);
}

39 数组中超过一半的数字

题目:数组中一个数字出现的次数出现的次数超过数组长度的一半。请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于2在数组中出现了5次,超过数组的一半,因此输出2.
思路:最好想的就是先排序,再找中位数,时间复杂度是O(nlogn).书上提出了一种时间复杂度为n的方法。按照快拍的思路,找到分界点,如果分界点比n/2小,那说明中位数在右边,大就在左边,正好就结束了。(找到这个数最好判断出现的次数大于一半在输出,懒得写了)

#include <stdio.h>
#include <stdlib.h>
#define bool unsigned int
#define true 1
#define false 0
#define none 2
#define INITLENGTH 40
#define INCREMENT 20

int Partition(int numbers[],int length,int start,int end)
{
	int key = numbers[start];
	while(start<end)
	{
		while(start<end && numbers[end]>=key)
			end--;
		numbers[start] = numbers[end];
		while(start<end && numbers[start]<=key)
			start++;
		numbers[end] = numbers[start];
	}
	numbers[start] =  key;
	return start;
}

int MoreThanHalfNum(int* numbers,int length)
{
	int middle = length>>2-1;
	int end = length-1;
	int start = 0;
	int index;
	if(numbers == NULL || length<0)return false;
	index = Partition(numbers,length,start,end);
	while(index!=middle)
	{
		if(index>middle)
		{
			end = index - 1;
			index = Partition(numbers,length,start,end);
		}
		else
		{
			start = index + 1;
			index = Partition(numbers,length,start,end);
		}
	}
	return numbers[index];

}

void main()
{
	int a[] = {1,2,3,2,2,2,5,4,2};
	printf("%d",MoreThanHalfNum(a,sizeof(a)/sizeof(int)));
}

思路二
根据这个数组的特点,这个数字出现的次数别其他所有数字出现的和还多。所以可以遍历数组,保存数字和次数,如果下一个与该数字相同,次数加一;不同,次数减一;当次数为0的时候,换数。所以最后剩下的肯定是要找的数。
你可以想象这个数是你的兵,不相同的是你的敌人,他们一对一肉搏,你的人多,最后肯定会剩下来的,当然对面还可能自相残杀,所以最后剩下的肯定是你的人。

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

int MoreThanHalfNum(int* numbers,int length)
{
	int result = 0;
	int count = 0;
	int i;
	if(numbers == NULL || length<0)return false;
	result = numbers[0];
	for(i=0;i<length;i++)
	{
		if(count == 0)
		{
			result = numbers[i];
		}
		if(numbers[i]==result)
			count++;
		else
			count--;
	}
	return result;
}

void main()
{
	int a[] = {1,2,3,2,2,2,5,4,2};
	printf("%d",MoreThanHalfNum(a,sizeof(a)/sizeof(int)));
}

40 最小的k个数

题目:输入n个数,找出其中最小的k个数。
思路:最好想的是排序,排序最快的是快排,时间复杂度是Nlog(N),书上提出两种新思路。
还是利用快排,但是不用把所有都排好。快排是把比一个数大的所有数放右,小的所有数放左边,如果这个数正好等于k,那左边的k个数就是排好了。比k大,就继续快排左边的,比k小,就排右边的。
(第二种太麻烦了 就不写了)

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

int Partition(int* numbers,int start,int end)
{
	int startIndex = start;
	int key = numbers[start];
	while(start<end)
	{
		while(start<end && numbers[end]>=key)end--;
		numbers[start] = numbers[end];
		while(start<end && numbers[start]<=key)start++;
		numbers[end] = numbers[start];
	}
	numbers[start] = key; 
	return start;
}

void GetLeastNumbers(int* input,int n,int* output,int k)
{
	int start;
	int end;
	int index;
	int i;
	if(input == NULL||output == NULL||k>n||n<=0||k<=0)
		return;
	start = 0;
	end = n-1;
	index = Partition(input,start,end);
	while(index!=k-1)
	{
		if(index>k-1)
		{
			end = index-1;
			index = Partition(input,start,end);
		}
		else
		{
			start = index+1;
			index = Partition(input,start,end);
		}
	}
	for(i=0;i<k;i++)
	{
		output[i] = input[i];
		printf("%d",input[i]);
	}
	
}

void main()
{
	int a[] = {8,7,6,5,4,3,2,1};
	int b[8];
	GetLeastNumbers(a,sizeof(a)/sizeof(int),b,sizeof(b)/sizeof(int));
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值