【剑指Offer】俯视50题之11 - 20题
面试题11 数值的整数次方
面试题12 打印1到最大的N位数
面试题13 在O(1)时间删除链表结点
面试题14 调整数组顺序使奇数位于偶数前面
面试题15 链表中倒数第k个结点
面试题16 反转链表
面试题17 合并两个排序的链表
面试题18 树的子结构
面试题19 二叉树的镜像
面试题20 顺时针打印矩阵
面试题11 数值的整数次方 实现函数 double pow(double bas,int exponent),求base的exponent次方,不得使用库函数。
代码例如以下:
int IsZero(double bas) { if((bas-0.0 < 0.0000001) && (bas-0.0 > -0.0000001 )) return 1; return 0; } double pow(double bas,int exponent) { int iSymbol = 1; double Result = 1; if (IsZero(bas) && exponent < 0) { throws exception; } if (exponent < 0) { iSymbol = -1; exponent = -exponent } while(exponent) { Result = Result*bas; exponent--; } if (iSymbol == 1) return Result; else return 1/Result; }优化之后的代码例如以下:
<pre name="code" class="html">int IsZero(double bas) { if((bas-0.0 < 0.0000001) && (bas-0.0 > -0.0000001 )) return 1; return 0; } double pow_Exponent(double bas, int exponent) { if (exponent == 0) return 1; if (exponent == 1) return bas; double result = pow_Exponent(bas,exponent>>1); result = result*result; if (exponent & 0x1 == 1) /* 假设最后一位是1*/ result = result * bas; return result; } double pow(double bas,int exponent) { int iSymbol = 1; double Result; if (IsZero(bas) && exponent < 0) { throws exception; } if (exponent < 0) { iSymbol = -1; exponent = -exponent } Result = pow_Exponent(bas, exponent); if (iSymbol == 1) return Result; else return 1/Result; }
面试题12打印1到最大的N位数。输入一个整数n,则打印从1到n位数999…9之间的数
实现例如以下:
#include <iostream> void printN(int iArray[], int n) { int i = 0; while(iArray[i] == 0) { i++; } for (; i < n; i++) { printf("%d",iArray[i]); } printf("\n"); } void PrintNum(int iArray[], int n, int iIndex) { if (iIndex == n) { printN(iArray,n); return; /* 函数出口。切记 */ } for (int i = 0; i <= 9; i++) { iArray[iIndex] = i; PrintNum(iArray, n, iIndex + 1); } } int main() { int iArray[3] = {0,0,0}; PrintNum(iArray, 2, 0); }
面试题13在O(1)时间删除链表结点
实现例如以下:
struct ListNode { int m_nValue; ListNode *m_pNext; }; void DeleteNode(ListNode **pListHead, ListNode *pToBeDeleted) { ListNode *pToBeDeletedTemp; if (pToBeDeleted == NULL || pListHead == NULL) return; if (pToBeDeleted->m_pNext != NULL) { pToBeDeletedTemp = pToBeDeleted->m_pNext; pToBeDeleted->m_nValue = pToBeDeletedTemp->m_nValue; pToBeDeletedTemp = pToBeDeletedTemp->m_pNext; free(pToBeDeletedTemp); } else if(*pListHead == pToBeDeleted)/* 仅仅有一个结点*/ { *pListHead = NULL; free(pToBeDeleted); } else /*最后一个结点。且含有多个结点的链表*/ { pToBeDeletedTemp = *pListHead; while(pToBeDeletedTemp ->m_pNext != pToBeDeleted) { pToBeDeletedTemp++; } pToBeDeletedTemp->m_pNext = NULL; free(pToBeDeleted); } }注意:
1. 考虑多个场景,分别进行处理
2. 记得释放删除结点的内存
面试题14 调整数组顺序使奇数位于偶数前面
思路:左右两个指针,左边假设是奇数则后移,右边假设是偶数则前移,然后交换顺序,直到遍历完数组。
实现例如以下:
void ReOrderOddEven(int iArray[],int n) { int iBegin = 0; int iEnd = n - 1; int iTemp; while(iBegin < iEnd) { while(iBegin < iEnd && iArray[iBegin]%2 != 0)/*奇数*/ { iBegin++; } while(iBegin < iEnd && iArray[iEnd]%2 == 0)/*偶数*/ { iEnd--; } if(iBegin < iEnd) { iTemp = iArray[iBegin]; iArray[iBegin] = iArray[iEnd]; iArray[iEnd] = iTemp; } } for (iBegin = 0; iBegin < n; iBegin++) { printf(" %d",iArray[iBegin]); } printf("\n"); }注意:
1. 假设方便扩展,则将推断条件抽象出一个函数,假设是奇数 == > Fun(n) 。不是奇数 ==> !Fun(n)
面试题15 链表中倒数第k个结点
实现例如以下:
struct ListNode { int m_nValue; ListNode *m_pNext; } ListNode * FindKFromTail(ListNode *pListHead, unsigned int k) { unsigned int num = k; ListNode *pList1 = pListHead; ListNode *pList2 = pListHead; if (pListHead == NULL || k == 0) return NULL; while(pList2->m_pNext != NULL && num - 1 != 0) { pList2 = pList2->m_pNext; } if (num != 0) { return NULL; } while(pList2) { pList1 = pList1->m_pNext; pList2 = pList2->m_pNext; } return pList1; }注意:
1. 注意函数的鲁棒性
面试题16 反转链表 。定义一个函数。输入链表的头结点,翻转链表之后。返回翻转之后的链表的头结点。实现例如以下:
struct ListNode { int m_nValue; ListNode *m_pNext; } ListNode * ReverseList(ListNode *pListHead) { ListNode *pListFront = pListHead; ListNode *pListNow = NULL; ListNode *pNext = NULL; if (pListFront == NULL) { return pListHead; } pListNow = pListFront->m_pNext; while (pListNow) { pNext = pListNow->m_pNext; pListNow->m_pNext = pListFront; pListFront = pListNow; pListNow = pNext; } return pListNow; }
面试题17合并两个排序的链表 。链表中没有反复的数字,且递增排列。
实现例如以下:
struct ListNode { int m_nValue; ListNode *m_pNext; } ListNode * Merge(ListNode *pListHead1, ListNode *pListHead2) { ListNode *pListHead; if (pListHead1 == NULL) return pListHead2; else if (pListHead2 == NULL) return pListHead1; if (pListHead1->m_nValue < pListHead2->m_nValue) { pListHead = pListHead1; pListHead->m_pNext = Merge(pListHead1->m_pNext,pListHead2); } else if (pListHead1->m_nValue > pListHead2->m_nValue) { pListHead = pListHead2; pListHead->m_pNext = Merge(pListHead2->m_pNext,pListHead1); } return pListHead; }
注意:
1. 注意使用递归思想考虑问题
面试题18 树的子结构 。给定两个树A和B,推断A是否是B的子结构,即B的某部分能够全然构成A
思路:
首先,推断从B中找到A的根节点
然后,递归比較B中该节点下面是否与A全然否相等。直到遍历完A为止。
实现例如以下:
struct BinaryTreeNode { int data; BinaryTreeNode *lchild; BinaryTreeNode *rchild; }; bool IsEqualTree(BinaryTreeNode *rootA, BinaryTreeNode *rootB) { if (rootB == NULL) { return true; } else if (rootA == NULL) { return false; } else if (rootA->data != rootB->data) { return false; } return (IsEqualTree(rootA->lchild, rootB->lchild)) && (IsEqualTree(rootA->rchild, rootB->rchild)); } bool IsSubTree(BinaryTreeNode *rootA, BinaryTreeNode *rootB) { bool result = false; if (rootA == NULL || rootB == NULL) { return result; } if (rootA->data == rootB) { result == IsEqualTree(rootA,rootB); } if (result == false) { result = IsSubTree(rootA->lchild, rootB); } if (rusult == false) { result = IsSubTree(rootA->rchild, rootB); } return result; }
面试题19 二叉树的镜像
两个互为镜像的二叉树,给一个数的根节点,将该二叉树转化为镜像二叉树
struct BinaryTreeNode { int data; BinaryTreeNode *lchild; BinaryTreeNode *rchild; }; void MirrorTree(BinaryTreeNode *root) { BinaryTreeNode *temp; if (root == NULL) { return; } MirrorTree(root->lchild); MirrorTree(root->rchild); temp = root->lchild; root->lchild = root->rchild; root->rchild = temp; } void MirrorTree(BinaryTreeNode * root) { BinaryTreeNode *temp; if (root == NULL) { return; } stack<BinaryTreeNode *> iStack; iStack.insert(root); while(iStack.size()) { if (root->lchild != NULL) { iStack.insert(root->lchild); } if (root->rchild != NULL) { iStack.insert(root->rchild); } temp = iStack->top()->lchild; iStack->top()->lchild = iStack->top()->rchild; iStack->top()->rchild = temp; iStack.pop(); } }
面试题20 顺时针打印矩阵
难点在于二维数组作为入參
实现例如以下:
#include <iostream> /* 从左到右输出数组,从上到下输出数组元素 */ void LToR_UToD(int *a, int iBeginA, int iBeginB, int iEndA, int iEndB,int n) { for (int i=iBeginB; i <= iEndB;++i) printf(" %d",a[iBeginA*n + i]); for (int j = iBeginA + 1; j <= iEndA; j++) printf(" %d", a[j*n + iEndB]); } /* 从右到左输出数组,从下到上输出数组元素 */ void RToL_DToU(int *a, int iBeginA, int iBeginB, int iEndA, int iEndB,int n) { for (int i=iEndB; i >= iBeginB;--i) printf(" %d",a[iEndA*n + i]); for (int j = iEndA - 1; j >= iBeginA; --j) printf(" %d", a[j*n + iBeginB]); } int main() { int a[][2]={{1,2},{3,4}}; int iBeginA = 0; int iBeginB = 0; int iEndA = 1; int iEndB = 1; while(iBeginA <= iEndA && iBeginB <= iEndB) { LToR_UToD((int *)a, iBeginA++, iBeginB, iEndA, iEndB--, 2); RToL_DToU((int *)a, iBeginA, iBeginB++, iEndA--, iEndB, 2); } printf("\n"); }
注意:
1. 二维数组作为入參数。怎样传递。
强制转化为指针
Func(a[][10]) 作为一个数组的形式传入
2. 对于一个m行n列的一个数组,a[i][j] 能够用指针表示为 a[i*n + j]