1.链表中倒数第K个结点
时间限制:1秒 空间限制:32768K 热度指数:429726
本题知识点:链表
题目描述
输入一个链表,输出该链表中倒数第k个结点
解题思路:
- 为了提高效率,显然我们最好只遍历一次便能找到链表中的倒数第K个结点
- 可以定义两个指针,两个指针一开始都指向链表的头结点
- 让第一个指针先走K-1步,第二工个指针保持不动
- 当第一个指针走到K步时,第二个指针也向前走一步。然后两个指针之间一直保持K-1的距离一直下去
- 当第二个指针到达链表的尾端时,第二个指针刚好指向倒数第K个结点
- 当然了要注意考虑边界值,链表是否为空,链表结点的个数是否小于K,K为0等可能造成程序崩溃的情况
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(NULL == pListHead || 0 == k)
return NULL;
ListNode* pFast = pListHead;
ListNode* pSlow = pListHead;
for(unsigned int i = 0; i < k-1; ++i)
{
if(pFast->next != NULL)
pFast = pFast->next;
else
return NULL;
}
while(pFast->next != NULL)
{
pFast = pFast->next;
pSlow = pSlow->next;
}
return pSlow;
}
};
2.反转链表
时间限制:1秒 空间限制:32768K 热度指数:310299
本题知识点: 链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
解题思路:
- 以这张图为例,我们假设4之前所有的节点已经反转成功
- 当我们反转4让它的next指向3之后,导致我们无法遍历到节点5,从而导致链表断开了
- 为了避免链表的断开,我们要在调整结点4之前把结点5保存下来,同时也需要知道4之前的一个结点
- 所以我们这里需要三个指针;当前遍历到的节点,它的前一个结点和后一个节点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode *pCur = pHead;
ListNode *pPrev = NULL;
ListNode* pNewHead = NULL;
//开始遍历并且进行逆置
while(pCur)
{
ListNode* pNext = pCur->next;
if(pNext == NULL)
{
pNewHead = pCur;
}
pCur->next = pPrev;
pPrev = pCur;
pCur = pNext;
}
return pNewHead;
}
};
3.合并两个排序的链表
时间限制:1秒 空间限制:32768K 热度指数:293834
本题知识点: 链表
扫描二维码关注公众号,回复: 2725039 查看本文章
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路:
- 将两个链表的头结点先进行比较。如果链表1的头结点小于链表2的头结点的值,所以链表1的头结点是合并后链表的头结点
- 在剩余的节点中,链表2的头结点和剩余链表1的头结点进行比较
- 如此下去,可以看出这是递归的一个过程
- 特殊情况:(1)如果链表1为空,那么合并后的结果就是第二个链表;(2)如果连表2为空,合并后的结果为链表1;(3)如果链表1和链表2均为空,合并的结果就是一个空链表
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(NULL == pHead1)
return pHead2;
if(NULL == pHead2)
return pHead1;
ListNode* pHeadMerge = NULL;
if(pHead1->val < pHead2->val)
{
pHeadMerge = pHead1;
pHeadMerge->next = Merge(pHead1->next, pHead2);
}
else{
pHeadMerge = pHead2;
pHeadMerge->next = Merge(pHead1, pHead2->next);
}
return pHeadMerge;
}
};
4.树的子结构
时间限制:1秒 空间限制:32768K 热度指数:286074
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
解题思路:
4
- 先检查在树1中的根节点是否和树2的根节点的值一样,如果一样,则判断在树1中的根节点的左子树是否和树2的左子树一样,发现不一样
- 接着判断树1的第二层进行判断,发现第二层的其中一个根节点和树2的根节点值一样,然后遍历这个根节点的左右子树发现和树2一样,此时我们便得到了一颗和树2结构一样的子树
- 然后按照相同的方式继续遍历树1
- 所以我们可以用递归的方式遍历,当然了也可以使用循环的方式去遍历
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if(NULL != pRoot1 && NULL != pRoot2)
{
if(Equal(pRoot1->val, pRoot2->val))
result = DoesTree1HaveTree2(pRoot1, pRoot2);
if(!result)
result = HasSubtree(pRoot1->left, pRoot2);
if(!result)
result = HasSubtree(pRoot1->right, pRoot2);
}
return result;
}
bool DoesTree1HaveTree2(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot2 == NULL)
return true;
if(pRoot1 == NULL)
return false;
if(!Equal(pRoot1->val, pRoot2->val))
return false;
return DoesTree1HaveTree2(pRoot1->left, pRoot2->left) && DoesTree1HaveTree2(pRoot1->right, pRoot2->right);
}
bool Equal(double num1, double num2)
{
if( (num1-num2) > -0.0000001 && (num1-num2) < 0.0000001)
return true;
return false;
}
};
5.二叉树的镜像:
时间限制:1秒 空间限制:32768K 热度指数:169702
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5
解题思路:
- 交换根节点的左右子树
- 交换值为10的结点的左右子树
- 交换值为6的结点的左右子树
- 求得一棵树的镜像的过程:先序遍历每个节点,如果遍历的节点有子结点,就交换它的两个子节点。当交换完所有的非叶子结点的左右结点之后,就得到了树的镜像
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
//判断树是否是一颗空树
if(NULL == pRoot)
return;
if( (NULL == pRoot->left) && (NULL == pRoot->right) )
return;
TreeNode* pTemp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = pTemp;
if(pRoot->left)
Mirror(pRoot->left);
if(pRoot->right)
Mirror(pRoot->right);
}
};
6.顺时针打印矩阵
时间限制:1秒 空间限制:32768K 热度指数:299701
本题知识点: 数组
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
解题思路:
- 如图是按照从外圈到内圈的顺序依次打印,且是顺时针打印。所以我们可以把矩阵想象成若干个圈。可以用一个循环来打印矩阵,每次打印矩阵中的一个圈
- 分析循环结束的条件:假设矩阵的行数是rows,列数是cols。打印第一圈的时候左上角的坐标是(0,0),第二圈左上角的坐标是(1,1),以此类推
- 可以发现左上角的坐标行标和列标是相同的,因为他们处于对角线上。所以可以选取左上角(start,start)的一圈作为我们分析的开始
- 对于5x5的矩阵而言,最后一圈只有一个数字,坐标为(2,2),可以发现 5 > 2x2;对于6x6的矩阵而言,最后一圈有四个数字,其中最左上角的数字坐标为(2,2),可以发现 6 > 2x2
- 所以循环继续的条件是cols>startY*2, rows>startY*2
- 然后就是考虑如何打印一圈的功能了。可以把打印一圈分成四步来做:(1)从左到右打印一行(2)从上到下打印一行(3)从右到左打印一行(4)从下往上打印一列
- 注意:最后一圈有可能是只有一行,只有一列,或者是只有一个数字,打印这样的一圈并不需要四步
- 仔细分析打印每一步的前提条件。第一步都是需要的,打印一圈至少要一步,但是如果只有一行那就不需要第二步了,
- 需要第二部步前提条件是:终止行号大于起始行号、
- 需要第三步的前提条件是:圈内至少需要两行两列,也就是要求终止行号大于起始行号,并且要求终止列号大于起始列号
- 需要第四步的前提条件是:至少有三行两列,所以要求终止行号大于起始行号至少为2,并且要求终止列号大于起始列号
# -*- coding:utf-8 -*-
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# write code here
if not matrix:
return None
rows = len(matrix)
cols = len(matrix[0])
start = 0
result = []
while rows > 2*start and cols > 2*start:
endx = rows - 1 - start # 每打印一次的时候的终止行号
endy = cols - 1 - start # 每打印一次的时候的终止列号
for i in range(start, endy+1): #打印第一行
result.append(matrix[start][i])
if start < endx: # 打印第二步
for i in range(start+1,endx+1):
result.append(matrix[i][endy])
if start < endx and start < endy: # 打印第三步
for i in range(endy-1, start-1, -1):
result.append(matrix[endx][i])
if start < endx-1 and start < endy: # 打印第四步
for i in range(endx-1, start, -1):
result.append(matrix[i][start])
start += 1
return result