剑指offer(C语言)22-30

22 链表中倒数第k个节点

题目:输入一个链表,输出该链表倒数第k个节点。为了符合多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,他们的值依次是1 2 3 4 5 6.这个链表的倒数第3个节点是只为4的节点。
思路:由于是单向链表,只有指向下一个的指针,没有指向上一个的指针。第一想法是先遍历链表,查出一共有n个节点,再遍历链表,找到第n-k+1个节点。但是还可以有一次遍历就能结束的更优化的想法:建立两个指针,第一个指针先走k-1步。第k步的时候两个指针一起走,等到第一个指针走到最后时,把第二个指针返回就好了。由于一直保证两个指针之间差k-1个节点,所以等到第二个节点到最后一个节点的时候,第一个节点就是倒数第k个。
注意:写代码要注意鲁棒性,不能因为一些错误的输入就导致程序崩溃。这道题有三种意外情况:
1.输入的头节点指针是NULL
2.k=0
3.k比总长度n要大
别的就没什么了 是难度偏低的一道题

#include <stdio.h>
#include <stdlib.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* FindKthToTail(listNode* head,unsigned int k)
{
	listNode* p1 = head;
	listNode* p2 = NULL;
	if(head == NULL || k==0)
		return NULL;
	while(k!=1)
	{
		p1=p1->next;
		k--;
		if(p1==NULL)
			return NULL;
	}
	p2 = head;
	while(p1->next!=NULL)
	{
		p1 = p1->next;
		p2 = p2->next;
	}
	return p2;
}
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);

	ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);

	//PrintList(pNode1);
	if(FindKthToTail(pNode1,k)!=NULL)
		printf("%d",FindKthToTail(pNode1,k)->data);
	else 
		printf("Error");
}

23 链表中环的入口节点

题目:如果一个链表中包含环,如何找出环的入口节点?例如在图中所示的链表中,环的入口节点是节点3
在这里插入图片描述思路:要找环的入口节点,首先要判断链表是否包含环。所以要把这道题分成两部分。
第一部分:如何判断该链表是否是环
如果是环一定是在环内不断循环,链表的地址是随机的,所以无法按地址递增来判断;链表中也会有循环的元素,也不可以通过找相同的元素来判断。所以可以用两个步伐不一样的指针,如果有环的话这两个指针总会指到同一个位置上。一个指针每次增1,另一个指针每次增2.当两个指针相同节点的时候说明指到了同一个环内节点上了。
第二部分:判断有环后,如何找到入口节点
在环内是一直循环的,周期是环内节点个数。所以两个指针如果差一个环内节点数,会有一个指针先进环内,第二个节点和这个节点相遇时,就是入口节点。所以第二部分又分两部分:确定环内节点个数找到入口节点
确定环内节点个数比较简单,第一部分已经得到一个环内的节点。从该节点不断向后找,再找到next的时候就指到环内节点个数了。找入口节点就按上面说的,设两个指针找就行了。

#include <stdio.h>
#include <stdlib.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* MeetingNode(listNode* head)
{
	listNode* pSlow;
	listNode* pFast;
	if(head==NULL)return NULL;
	pSlow = head->next;
	if(pSlow==NULL)return NULL;
	pFast = pSlow->next;
	while( pFast != NULL)
	{
		if(pFast == pSlow)
			return pSlow;
		pSlow = pSlow->next;
		pFast = pFast->next;
		if(pFast == NULL)return NULL;
		pFast = pFast->next;
	}
	return NULL;
}

listNode* EntryNodeOfLoop(listNode* head)
{
	listNode* meetingNode = MeetingNode(head);
	listNode* next = NULL;
	listNode* pNode1 = NULL;
	listNode* pNode2 = NULL;
	
	int loopCount = 0;
	if(meetingNode == NULL)return NULL;
	next = meetingNode->next;
	loopCount++;
	while(next!= meetingNode)
	{
		next = next->next;
		loopCount++;
	}
	pNode2 = head;
	pNode1 = head;
	for(;loopCount>0;loopCount--)
	{
		pNode1 = pNode1->next;
	}
	while(pNode1->data!=pNode2->data)
	{
		pNode1 = pNode1->next;
		pNode2 = pNode2->next;
	}
	return pNode1;
}

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

	ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);
	ConnectListNodes(pNode5, pNode3);

	if(EntryNodeOfLoop(pNode1)!=NULL)
		printf("%d",EntryNodeOfLoop(pNode1)->data);
	else
		printf("Error");
	//PrintList(pNode1);
}

24 反转链表

题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
思路:自己写一个链表,抛开程序用语言描述一下该怎么做。
在这里插入图片描述首先需要一个指针pNode指向被改变的节点。由于链表是单向的,所以需要一个preNode记录被改变节点的前一个节点。
从a开始,把a的next变成前面的(因为是头节点,所以是NULL)。这时候应该改b了,但是a和b之间已经没联系了。所以在断连接之前还需要一个nextNode指向a的next。
pNode现在指向b,preNode指向a。1.先把nextNode指向c 2.b(pNode)的next指向a(preNode) 3.preNode指向pNode 4.pNode 指向nextNode.
结束的标志是pNode->next是NULL.这时候说明pNode是原来的尾节点了。这时候跳出循环,把pNode指向preNode,然后把pNode返回就好了。

#include <stdio.h>
#include <stdlib.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* ReserveList(listNode* head)
{
	listNode* pNode = head;
	listNode* preNode = NULL;
	listNode* nextNode = NULL;
	if(head == NULL)return NULL;
	while(pNode->next!=NULL)
	{
		nextNode = pNode->next;
		pNode->next = preNode;
		preNode = pNode;
		pNode = nextNode;
	}
	pNode->next = preNode;
	return pNode;

}

void main()
{
	listNode* pNode1 = CreateListNode(1);
    listNode* pNode2 = CreateListNode(2);
    listNode* pNode3 = CreateListNode(3);
    listNode* pNode4 = CreateListNode(4);
    listNode* pNode5 = CreateListNode(5);

	ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);

	PrintList(pNode1);
	printf("\n");
	PrintList(ReserveList(pNode1));
}

25 合并两个排序的链表

题目:输入两个递增排序的链表,合并这两个链表并使更新链表中的节点仍然是递增排序的。
思路:这道题可以用递归和非递归两种形式来做,先将非递归。
首先需要两个指针pNode1和pNode2分别指向两个输入链表。还需要一个pNode3和pHead3用来生成新的链表。先判断pNode1和pNode2谁大谁小,选出小的。之后再判断pHead3是不是指向NULL,如果指向NULL,就先将pNode3指向刚才选出的,然后把pHead指向pHead;如果pHead3不指向NULL,就把pNode3指向选出的,然后pNode3 = pNode3->next;
循环条件是pNode1和pNode2都不为空,当跳出循环后说明有一个是空的,这时候说明另一个不为空的链表可以直接接过来。然后把pHead3返回就好了。

#include <stdio.h>
#include <stdlib.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* Merge(listNode* pHead1,listNode* pHead2)
{
	listNode* pNode1 = pHead1;
	listNode* pNode2 = pHead2;
	listNode* pHead3 = NULL;
	listNode* pNode3 = NULL;
	if(pNode1==NULL)return pHead2;
	if(pNode2==NULL)return pHead1;
	while(pNode1!=NULL && pNode2!=NULL)
	{
		if(pNode1->data<pNode2->data)
		{
			if(pHead3 == NULL)
			{
				pNode3 = pNode1;
				pHead3 = pNode3;
			}
			else
			{
				pNode3->next = pNode1;
				pNode3 = pNode3->next;
			}
			pNode1 = pNode1->next;
		}
		else
		{
			if(pHead3 == NULL)
			{
				pNode3 = pNode2;
				pHead3 = pNode3;
			}
			else
			{
				pNode3->next = pNode2;
				pNode3 = pNode3->next;
			}
			pNode2 = pNode2->next;
		}
	}
	if(pNode1 == NULL)
		pNode3->next = pNode2;
	else
		pNode3->next = pNode1;
	return pHead3;
}
void main()
{
	listNode* pNode1 = CreateListNode(1);
    listNode* pNode2 = CreateListNode(3);
    listNode* pNode3 = CreateListNode(5);
    listNode* pNode4 = CreateListNode(7);
    listNode* pNode5 = CreateListNode(9);

	ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);

	PrintList(Merge(pNode1,NULL));
}

思路2:这道题也可以通过递归的方式来实现,代码会更简洁一点。上面的代码要判断一下是否头节点已经指向了某一结点,指向和没指向是两种不同的算法。但是用递归,每次头节点都没指向节点,比较完大小时候,把该节点指向小的那个节点之后,然后该节点指向继续递归除掉那个之后的两个链表的合并。。。说的比较绕,自己写写思路还是挺清晰的。

listNode* Merge(listNode* pHead1,listNode* pHead2)
{
	listNode* pHead3 = NULL;
	if(pHead1==NULL)return pHead2;
	if(pHead2==NULL)return pHead1;

	if(pHead1->data < pHead2->data)
	{
		pHead3 = pHead1;
		pHead1->next = Merge(pHead1->next,pHead2);
	}
	else
	{
		pHead3 = pHead2;
		pHead2->next = Merge(pHead1,pHead2->next);
	}
	return pHead3;
}

26 树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构。
思路:树的结构比链表还要复杂,因为既有左子树又有右子树。所以树相关的问题基本上都用递归来做
这道题可以分为两部分:1.遍历二叉树A,找到和B根节点相等的节点 2.找到相等的节点后从该节点出发,逐个比较树中的所有节点。直至二叉树的左树和右数都是NULL,证明完全相等。
第一部分:
先判断两个的value相不相等,相等就进入第二部分;不相等就递归A的左孩子和B;递归A的右孩子和B
第二部分:
由于B的任务就是把B比较至NULL,A是NULL说明A不够了,失败。所以先判断B是不是NULL,是就返回成功;再判断A,是NULL就返回失败。如果A和B的value相等,递归A的左孩子和B的左孩子,A的右孩子和B的右孩子,两个最后递归的结果都是true才能返回true,用&&操作。
备注
1.malloc是在堆中申请内存,用完记得释放掉,free后把指针指向NULL,不然就是野指针了。因为要改变指针指向的位置,所以子函数要传入二级指针。
2.这道题是double类型,所以比较大小不能用==比较,要设置一个比较小的值(比如e-9),判断两个数的差值是不是比这个值小。比较懒,知道意思就行了,没这么做。

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

bool DoesTree1HasTree2(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
	if(pRoot2 == NULL)return true;
	if(pRoot1 == NULL)return false;
	if(pRoot1->value == pRoot2->value)
	{
		return DoesTree1HasTree2(pRoot1->left,pRoot2->left)&&
			   DoesTree1HasTree2(pRoot1->right,pRoot2->right);
	}
	else
		return false;
}
bool HadSubTree(BinaryTreeNode *pRoot1,BinaryTreeNode *pRoot2)
{
	bool result = false;
	if(pRoot1!=NULL && pRoot2!=NULL)
	{
		if(pRoot1->value == pRoot2->value)
			result = DoesTree1HasTree2(pRoot1,pRoot2);
		if(!result)
			result = HadSubTree(pRoot1->left,pRoot2);
		if(!result)
			result = HadSubTree(pRoot1->right,pRoot2);
	}
	return result;
}

27 二叉树的镜像

题目:请完成一个函数,输入一棵二叉树,该函数输入他的镜像。
思路:还是一样,跟二叉树有关的,先考虑递归。所以镜像就是遍历所有二叉树,然后把左孩子和右孩子交换一下即可。很简单的一道题
注意:考虑边界和特殊情况,其实只需要进函数的时候判断一下是不是NULL就可以了。书上还比较一下左孩子和右孩子是不是都为空,其实不加这句话,再递归两次也会返回,所以没什么意义。只需要在->之前判断这个是不是NULL就可以了。

#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 MirrorTree(BinaryTreeNode*pRoot)
{
	BinaryTreeNode* temp;
	if(pRoot==NULL)return;
	temp = pRoot->left;
	pRoot->left = pRoot->right;
	pRoot->right = temp;
	MirrorTree(pRoot->left);
	MirrorTree(pRoot->right);
}

void main()
{
	/*****************TREE 1 ***********************/
	BinaryTreeNode* pNodeA1 = CreateBinaryTreeNode(1);
    BinaryTreeNode* pNodeA2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNodeA3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNodeA4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNodeA5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNodeA6 = CreateBinaryTreeNode(6);
    BinaryTreeNode* pNodeA7 = CreateBinaryTreeNode(7);

	ConnectTreeNodes(pNodeA1, pNodeA2, pNodeA3);
    ConnectTreeNodes(pNodeA2, pNodeA4, pNodeA5);
    ConnectTreeNodes(pNodeA3, pNodeA6, NULL);
	
	MirrorTree(pNodeA1);

	DestroyTree(&pNodeA1);
}

28 对称的二叉树

题目:请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
思路:还是递归,递归函数负责比较输入的两个节点的数值。比较相等之后再递归一个的左节点和另一个的右节点,一个的右节点和另一个的左节点。俩个递归要同时为真才可以。
结束条件:当两个都是NULL就返回TRUE,只有一个NULL返回FALSE。中途发现比较失败时直接返回FALSE。

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

bool isSymmetrical(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
	if(pRoot1 == NULL && pRoot2 == NULL)
		return true;
	if(pRoot1 == NULL || pRoot2 == NULL)
		return false;
	if(pRoot1->value == pRoot2->value)
	{
		return isSymmetrical(pRoot1->left,pRoot2->right)
			&& isSymmetrical(pRoot1->right,pRoot2->left);
	}
	else
		return false;
}

void main()
{
	/*****************TREE 1 ***********************/
	BinaryTreeNode* pNodeA1 = CreateBinaryTreeNode(1);
    BinaryTreeNode* pNodeA2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNodeA3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNodeA4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNodeA5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNodeA6 = CreateBinaryTreeNode(6);
    BinaryTreeNode* pNodeA7 = CreateBinaryTreeNode(7);

	BinaryTreeNode* pNodeB1 = CreateBinaryTreeNode(1);
    BinaryTreeNode* pNodeB2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNodeB3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNodeB4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNodeB5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNodeB6 = CreateBinaryTreeNode(6);
    BinaryTreeNode* pNodeB7 = CreateBinaryTreeNode(7);

	ConnectTreeNodes(pNodeA1, pNodeA2, pNodeA3);
    ConnectTreeNodes(pNodeA2, pNodeA4, pNodeA5);
    ConnectTreeNodes(pNodeA3, pNodeA6, pNodeA7);

	ConnectTreeNodes(pNodeB1, pNodeB3, pNodeB2);
    ConnectTreeNodes(pNodeB2, pNodeB5, pNodeB4);
    ConnectTreeNodes(pNodeB3, pNodeB7, pNodeB6);
	
	printf("%d",isSymmetrical(pNodeA1,pNodeB1));

	DestroyTree(&pNodeA1);
	DestroyTree(&pNodeB1);
}

29 顺时针打印矩阵

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
思路:这是不涉及复杂数据结构的一道题,接替的关键就是把题目弄清楚。
顺时针打印,就是从最左上角开始打印一圈,然后横坐标加一纵坐标加一,再打印一圈。直至结束。由于画圈起点的横坐标和纵坐标总相等,所以可以把这个数定义为start.所以第一个问题是:什么时候停止?
这个我拍脑袋没想出来,就在纸上画了画。
1×1的矩阵,当start=0时打印,1时停止;
2×2的矩阵,当start=0时打印,1时停止;
3×3的矩阵,当start=0,1时打印,2时停止;
4×4的矩阵,当start=0,1时打印,2时停止;
找到了规律,start停止是有周期的,周期是2.随便推理一下就可以得到,结束条件是start2>=columns(或rows),这是对于正方形矩阵而言。那么长方形呢?
2×3的矩阵,当start=0时打印,1时停止;
3×2的矩阵,当start=0时打印,1时停止;
又找到了规律,结束条件是 start
2>=columns || start*2>=rows
第二个问题:如何打印一圈?
一圈有四条边,分别打印四个边就好了。
1.纵坐标为start不变,横坐标从start开始到columns-1-start结束
2.横坐标为columns-1-start不变,纵坐标从start+1开始,到rows-1-start结束
3.纵坐标为rows-1-start不变,横坐标从columns-1-start-1开始,到start结束
4.横坐标为start不变,纵坐标从rows-2-start开始,到start+1结束
对于常规情况而言,这道题似乎解决了,但是要考虑一下边界条件。
当只有一行的情况下(1234):1打印1234,2条件判定失败,3打印321,4条件判定失败。打印出来是1234321
当只有一列的情况下(1234):1打印了一个1,2打印了234,3条件判定失败,4打印32。打印出来是123432
当只有一行一列的情况下没什么问题。
可以看出,出问题都是在34步出的问题,只有一行是第3步出现问题,只有一列是第4步出现问题。所以在34步前面加一下判断就可以了
备注:C++在二维数组作为二级指针传入子函数之后,还可以用a[][]的形式进行操作,但是C语言会直接报错。

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

void PrintClockWisely(int** numbers,int columns,int rows)
{
	int start = 0;
	int column = start;
	int row = start;
	int endX;
	int endY;
	if(numbers == NULL || columns<=0 || rows <= 0)
		return ;
	while(start*2<columns && start*2<rows)
	{
		endX = columns-start-1;
		endY = rows - 1 - start;
		for(column = start;column<=endX;column++)
		{
			printf("%d ",numbers[columns*start + column]);
		}
		for(row = start+1;row<=endY;row++)
		{
			printf("%d ",numbers[columns*row + endX]);
		}
		if(start < endY)
		{
			for(column = endX-1;column>=start;column--)
			printf("%d ",numbers[columns*endY + column]);
		}
		if(start < endX)
		{
			for(row = endY-1;row>=start+1;row--)
			printf("%d ",numbers[columns*row + start]);
		}
		start++;
	}
}

void main()
{
	int number[][4]= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
	//int number[][4] = {1,2,3,4};
	int columns = sizeof(number[0])/sizeof(int);
	int rows = sizeof(number)/sizeof(number[0]);
	PrintClockWisely(number,columns,rows);
}

30 包含min函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能得到栈的最小元素的min函数。在该栈中,调用min push pop的时间复杂度都是O(1).
思路:读明白题,得到最小值不是出栈最小值,由于栈先进后出的顺序是不能改变的所以不能在原栈上做手脚。而最小的值是随时有可能出栈的,所以也不可以定义一个变量记录当前的最小值。要保证在每个出栈后都更新最小值。所以应该在建立一个辅助栈,在同等位置保证该位置的数是当前的最小值。
其实想明白了挺简单的,建立一个辅助栈,在数据栈进数的时候,辅助栈也进,如果进的数比栈顶还小(栈顶一直是当前最小值),就入栈这个数,如果不比栈顶小,就把栈顶再入一次,这就保证了辅助栈的栈顶一直是当前数据栈的最小值。

#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 PushMin(stack* stack1,stack* minStack,int data)
{
	if(stack1 == NULL || minStack == NULL)
		return false;
	Push(stack1,data);
	if(minStack->top == minStack->base || *(minStack->top-1)>data)
		Push(minStack,data);
	else
		Push(minStack,*(minStack->top-1));
}

int PopMin(stack* stack1,stack* minStack)
{
	Pop(minStack);
	return Pop(stack1);
}
int MinInStack(stack* stack1,stack* minStack)
{
	if(stack1->top!=stack1->base && minStack->top!=minStack->base)
		return *(minStack->top-1);
	return 0;
}
void main()
{
	int i =0;
	stack* stack1 = InitStack();
	stack* minStack = InitStack();
	for(i=0;i<40;i++)
	{
		PushMin(stack1,minStack,40-i);
	}
	for(i=0;i<40;i++)
	{
		printf("%d\n",MinInStack(stack1,minStack));
		PopMin(stack1,minStack);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值