day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点

文章目录

  • 1. JZ15 二进制中1的个数
    • bitset运用
    • 位运算-右移/左移
    • 位运算-与操作-阿秀题解
  • JZ16 数值的整数次方
    • 数学计算模拟过程
    • 快速幂运算-二分法应用
    • 幂运算的理解
  • 3. 调整数组顺序使奇数位于偶数前面
    • 暴力解法
    • 原地解法
  • 4. 链表中倒数第k个结点
    • 常规解法
    • 快慢指针
  • 5. JZ24 反转链表
    • 前后指针
    • 头插法
  • 6. JZ25 合并两个排序的链表
    • 迭代法
    • 递归法 不符合要求
  • 7. JZ26 树的子结构
  • 8. JZ27 二叉树的镜像
    • 递归
    • 队列迭代
    • 栈迭代
  • 9. JZ29 顺时针打印矩阵
  • 10. JZ30 包含min函数的栈
    • 两个栈
    • 一个栈
  • 补充内容:bitset的用法
    • bitset的用法

1. JZ15 二进制中1的个数

题目描述:输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示

bitset运用

利用递增,右上角为分割点

  class Solution {
public:
    int NumberOf1(int n) {
        //bitset运用 将n转化为 32位二进制数表示 不足32位的前面补齐即可 count()二进制数中1的个数
        return bitset<32>(n).count();
    }
};

位运算-右移/左移

class Solution {
public:
    int NumberOf1(int n) {
        //右移运算
        int result = 0;
        for(int i=0; i<32; i++)
        {
           if((n & 1)!=0) result++;
           n = n>>1;
            //if((n & (1 << i)) != 0) result++;//左移
        }
        return result;
    }
};

位运算-与操作-阿秀题解

思路:n&(n−1)n&(n-1)n&(n−1),会将n的二进制中最低位由1变成0

如果一个整数不为0,那么这个整数至少有一位是1。如果把这个数减1,那么原来处在整数最右边的1就会变为0,如果最右边的1后面还有0的话,原来在1后面的所有的0都会变成1。其余所有位将不会受到影响。

举个例子: 一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011。也就是说减1的结果是把最右边的一个1开始的所有位都取反了。

这时,如果再把原来的整数和减去1之后的结果做与运算,来整数最右边的1之后的所有位都会变成0,如1100&1011=1000。也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第1张图片

class Solution {
public:
    int NumberOf1(int n) {
        int result = 0;
        //与操作
        while(n)
        {
            n = n & (n-1);
            result++;
        }
        return result;
    }
};

JZ16 数值的整数次方

题目:实现函数 double Power(double base, int exponent),求base的exponent次方。
给定一个double类型的浮点数base和int类型的整数exponent,保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数

方法都是看题解的

数学计算模拟过程

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第2张图片

class Solution {
public:
    double Power(double base, int exponent) {
    if(base == 0.0) return 0;
    if(exponent == 0) return 1;
    //数学计算
    bool flag = false;
    if(exponent < 0)
    {
        flag = true;
        exponent *= -1;
    }
    double result = base;
    for(int i=1; i<exponent; i++)
    {
        result *= base;
    }
    if(flag) return 1.0 / result;
    return result;
    }
};

快速幂运算-二分法应用

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第3张图片day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第4张图片
day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第5张图片

幂运算的理解

以下内容搬运力扣大佬的题解,很清晰,作者是jyd,来源力扣,链接

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第6张图片 day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第7张图片 day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第8张图片 day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第9张图片

也是在模拟幂运算的过程,如果是偶数次方幂运算,底数扩大2倍,幂数就要缩小2倍;如果是奇数次方幂运算,取出一个底数,就变成了偶数次方的幂运算了,过程一样。所以才会有幂数对2取余数( 判断二进制数最右一位值)的操作了。

注意点:

  1. long exp = exponent;这一步原因如下:

这里是因为计算机内int的范围问题。在普通计算机内,负数都是补码表示的。int32的范围是[-2147483648, 2147483648],但int是无法正常表示-2147483648的补码,计算机里-2147483648的补码和2147483648的补码一样,这是因为溢出了,表示不了,而在long类型下可以正常表示-2147483648。

  1. if(exponent < 0) exp = exponent * (-1.0);这一步原因如下:

exponent == INT_MIN时,对应的相反数是大于INT_MAX的,所以要用一个大于 INT_MAX的类型来保存。把此时的exponent转正数时, exponent*(-1)的结果依然是int类型(隐藏类型),然后将结果赋值给 exp。但用来保存结果值的不应该是个int型,因此用double型的-1(-1.0) ,这样就可以将相乘的结果值保存为一个 double类型了,然后再进行赋值。

class Solution {
public:
    double Power(double base, int exponent) {
    if(base == 0.0) return 0;
    if(exponent == 0) return 1;
    //快速幂运算
    long exp = exponent;
    if(exponent < 0) 
    {
        exp = exponent * (-1.0);
    }
    double result = 1.0;
    while (exp != 0) 
    {
        //exp对2取余数 等价于 判断二进制数最右一位值是0还是1,即exp & 1
        if((exp & 1) == 1) result *= base;//奇数次方时,可以再往上乘一个
        base *= base;//叠加
        //exp对2向下取整 等价于 右移一位,即exp >> 1
        exp = exp >> 1;//减少乘次数
    }
    return exponent < 0 ? 1.0/result : result;
    }
};

3. 调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
对应力扣922题,写过,第一时间反应过来的是暴力解法

暴力解法

一个数组,先保存奇数,再保存偶数。时间/空间都是 O ( n ) O(n) O(n)

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> result(array.size(), 0);
        int count = 0;
        for(int i = 0; i<result.size(); i++)
        {
            if((array[i] % 2) != 0) result[count++] = array[i];
        }
        for(int j = 0; j<array.size(); j++)
        {
            if((array[j] % 2) == 0) result[count++] = array[j];
        }
        array.assign(result.begin(), result.end());
    }
};
  • 优化空间,只保存偶数,辅助数组的大小为原数组的一半
class Solution {
public:
    void reOrderArray(vector<int> &array) {
        //暴力解法-优化
        int len = array.size();
        int oddindex = 0, evenindex = 0;//奇偶数的个数
        vector<int> temp(len / 2 + 1, 0);
        for(int i=0; i<array.size(); i++)
        {
            if((array[i] & 1) == 1) array[oddindex++] = array[i];//奇数直接存在原数组
            else temp[evenindex++] = array[i];//偶数存在辅助数组
        }
        for(int j=0; j<evenindex; j++)
        {
            array[j + oddindex] = temp[j];
        }
    }
};

原地解法

不使用辅助数组,在原数组修改,但是时间复杂度是 O ( n 2 ) O(n^2) O(n2),空间是 O ( 1 ) O(1) O(1),空间换时间

如果两个相邻的数是前偶后奇,就交换,双层循环是从两头向中间逼近,外循环从前,内循环从外

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        //原地解法
        for(int i=0; i<array.size(); i++)//外循环向中间逼近
        {
            for(int j=array.size()-1; j>i; j--)//内循环向中间逼近
            {
                if((array[j-1] & 1) == 0 && (array[j] & 1) == 1)//前面是偶数,后面是奇数
                {
                    swap(array[j-1], array[j]);
                }
            }
        }
    }
};

4. 链表中倒数第k个结点

题目:输入一个链表,输出该链表中倒数第k个结点。

常规解法

求总结点数len,然后正序遍历len-k,找到倒数第k个结点

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
		int len = 0;
		ListNode* dummyhead = pListHead;
		while (dummyhead) 
		{
			dummyhead = dummyhead->next;
			len++;
		}
		len = len - k;
		if(len < 0) return nullptr;
		dummyhead = pListHead;
		while(len--)
		{
			dummyhead = dummyhead->next;
		}
		return dummyhead;
    }
};

快慢指针

pListHead相当于是快指针,先走k步,然后慢指针和快指针同时开始走,要注意慢指针走到倒数第k个位置时,快指针还没有走到头。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
		//快慢指针
		ListNode* slownode = pListHead;
		//pListHead相当于是快指针,先走k步
		while (k) //k 一直走到 0 即可
		{
			k--;
			//在其中判断是否出现k 大于链表总长度的情况 比如 【1,2,3,4,5】 6这样的情况,如果出现这样的情况,返回即可
			if(!pListHead) return nullptr;
			pListHead = pListHead->next;
		}
		while (pListHead) //先走的不能为空
		{
			slownode = slownode->next;
			pListHead = pListHead->next;
		}
		//if(slownode->next) slownode->next = nullptr;
		return slownode;	
    }
};

5. JZ24 反转链表

题目描述:完成队列的Push和Pop操作。 队列中的元素为int类型给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第10张图片

前后指针

用一个指针暂存下一个要反转的结点,前指针指向后面的指针进行反转。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* ReverseList(ListNode* head) {
        if (head == nullptr || head->next == nullptr) return head;
        //头插法 前后指针
        ListNode* pre = nullptr;
        ListNode* cur = head;
        ListNode* temp = nullptr;
        while(cur)
        {
            temp = cur->next;//保存cur下一个结点,下一个要反转的结点
            cur->next = pre;//翻转
            pre = cur;//更新
            cur = temp;//更新
        }
        return pre;
    }
};

头插法

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* ReverseList(ListNode* head) {
        if (head == nullptr || head->next == nullptr) return head;
        // 头插法
        ListNode* pre = new ListNode(0);
        ListNode* cur = head->next;
        ListNode* temp = nullptr;
        //head -> 1 -> 2 -> 3 -> null
        pre->next = head;//pre新链表的虚拟头结点 pre->head
        head->next = nullptr;//新链表是以null开始的,相当于pre -> null,即pre->next新链表是真正表头 
        while (cur) {
            temp = cur;//保存当前结点 后面要让pre更新 temp=1
            cur = cur->next;//cur为下一个反转结点 cur=2
            //此时pre=null temp=1, cur=2
            temp->next = pre->next;//相当于1 -> null
            pre->next = temp;//pre=1
            //此时1 -> null
        }
        return pre->next;
    }
};

6. JZ25 合并两个排序的链表

题目描述:输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。要求空间复杂度 O(1),时间复杂度 O(n)。

双指针+虚拟表头,注意新链表指针后移

迭代法

空间复杂度 O(1),时间复杂度 O(n+m)

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead1 ListNode类 
     * @param pHead2 ListNode类 
     * @return ListNode类
     */
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == nullptr) return pHead2;
        if(pHead2 == nullptr) return pHead1;
        ListNode* dummyhead = new ListNode(0);
        ListNode* cur = dummyhead;
        while(pHead1 && pHead2)
        {
            if(pHead1->val > pHead2->val) swap(pHead1, pHead2);//选结点值较小的
            cur->next = pHead1;//新链表连接结点
            //结点更新
            pHead1 = pHead1->next;//pHead1的结点用掉了 要更新
            cur = cur->next;//指针后移,否则从新链表头结点直接连下一个结点
        }
        //是否遍历完 奇数长度的链表还剩下一个结点没有连接
        cur->next = pHead1 ? pHead1 : pHead2;
        return dummyhead->next;//去掉新链表的表头
    }
};

递归法 不符合要求

时间、空间都是O(n+m),扩展思路

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead1 ListNode类 
     * @param pHead2 ListNode类 
     * @return ListNode类
     */
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        //递归法 不符合要求
        //递归结束条件
        if(pHead1 == nullptr || pHead2 == nullptr)
        {
            return pHead1 ? pHead1 : pHead2;//返回不为空的链表
        }
        //选择结点值较小的作为新链表结点
        if(pHead1->val <= pHead2->val)
        {
            //pHead1结点值较小 连接下一个(两个链表中结点值小的)新结点
            //问题由1->3->5 2->4->6的合并 变成了3->5 2->4->6 的合并
            pHead1->next = Merge(pHead1->next, pHead2);//注意传入的链表
            return pHead1;
        }
        else
        {
            pHead2->next = Merge(pHead2->next, pHead1);
            return pHead2;
        }
    }
};
class Solution {
public:
    int minNumberInRotateArray(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        //二分法 左闭右开 最值在两头
        int left = 0, right = nums.size()-1;
        while(left+1 < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right]) right = mid;//说明右边有序,那就向左边走
            else if(nums[mid] == nums[right]) right = right-1;
            else left = mid;
        }
        return min(nums[left], nums[right]);
    }
};

7. JZ26 树的子结构

题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)。假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构。

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第11张图片
搬运力扣Krahets的题解:

  1. isSubStructure(A, B) 函数:
  • 特例处理: 当 树 A 为空 或 树 B 为空 时,直接返回 false ;
  • 返回值: 若树 B 是树 A 的子结构,则必满足以下三种情况之一,因此用或 || 连接;
    • 以 节点 A 为根节点的子树 包含树 B ,对应 recur(A, B);
    • 树 B 是 树 A 左子树 的子结构,对应 isSubStructure(A.left, B);
    • 树 B 是 树 A 右子树 的子结构,对应 isSubStructure(A.right, B);
    • 实质上是在对树 A 做 先序遍历 。
  1. recur(A, B) 函数:
  • 终止条件:
    • 当节点 B 为空:说明树 B 已匹配完成(越过叶子节点),因此返回 true ;
    • 当节点 A 为空:说明已经越过树 A 叶子节点,即匹配失败,返回 false ;
    • 当节点 A 和 B 的值不同:说明匹配失败,返回 false ;
  • 返回值:
    • 判断 A 和 B 的左子节点是否相等,即 recur(A.left, B.left) ;
    • 判断 A 和 B 的右子节点是否相等,即 recur(A.right, B.right) ;

这道题和力扣的 572. 另一个树的子树类似,力扣认为空树也是子树结构,上次写的这道题的记录,对称二叉树的相关题

/*
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) {
		//如果两个树有一个是空树 都不符合要求
		if(pRoot1 == nullptr || pRoot2 == nullptr) return false;
		//三种情况 p2是p1的左子树、p2是p1的右子树、p2从根节点就是p1的子树 有一个成立即可 或条件 
		//前序遍历 另一个函数判断子树结构
		return isSametree(pRoot1, pRoot2) || HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);
    }

	bool isSametree(TreeNode* A, TreeNode* B)
	{
		//判断B是否为A子树结构 逐个结点判断
		//如果B是空结点,递归结束;A是空结点,或者 A、B结点值不相等 不合要求;依次左、右结点比较
		if(B == nullptr) return true;
		if(A == nullptr) return false;
		if(A->val == B->val) return isSametree(A->left, B->left) && isSametree(A->right, B->right);
		else return false;
	}
};

8. JZ27 二叉树的镜像

题目描述: 操作给定的二叉树,将其变换为源二叉树的镜像。
数据范围:二叉树的节点数 0≤n≤10000 , 二叉树每个节点的值 0≤val≤10000
要求: 空间复杂度 O(n) 。本题也有原地操作,即空间复杂度 O(1)的解法,时间复杂度 O(n)。

day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第12张图片

递归

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        //递归
        if(pRoot == nullptr) return nullptr; //结束
        swap(pRoot->left, pRoot->right);
        Mirror(pRoot->left);
        Mirror(pRoot->right);
        return pRoot;
    }
};

队列迭代

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        if(pRoot == nullptr) return nullptr;
        //队列迭代
        queue<TreeNode*> que;
        que.push(pRoot);
        while(!que.empty())
        {
            TreeNode* cur = que.front();//中
            que.pop();
            if(cur)
            {
                //存当前节点的左、右子节点 然后交换
                que.push(cur->left);
                que.push(cur->right);
                swap(cur->left, cur->right);
            }
        }
        return pRoot;

    }
};

栈迭代

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        if(pRoot == nullptr) return nullptr;
        //栈迭代
        stack<TreeNode*> st;
        st.push(pRoot);
        while(!st.empty())
        {
            TreeNode* cur = st.top();
            st.pop();
            if(cur)
            {
                st.push(cur->left);
                st.push(cur->right);
                swap(cur->left, cur->right);
            }
        }       
        return pRoot;
    }
};

9. JZ29 顺时针打印矩阵

题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
day2-牛客67道剑指offer-JZ15、JZ16、JZ24、JZ25、JZ26、JZ27、JZ29、JZ30、调整数组顺序使奇数位于偶数前面、链表中倒数第k个结点_第13张图片

四个方向模拟,注意越界判断

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> result;
        if(matrix.empty()) return result;
        int rl = 0, rh = matrix.size() - 1;//上下边界 行 记录待打印的矩阵上下边缘
        int cl = 0, ch = matrix[0].size() - 1;//左右边界 列 记录待打印的矩阵左右边缘
        while(1)
        {
            for(int i=cl; i<=ch; i++) result.push_back(matrix[rl][i]);//固定上边界 行 从左往右
            if(rl++ > rh) break;//操作行 防止越界
            for(int i=rl; i<=rh; i++) result.push_back(matrix[i][ch]);//固定右边界 列 从上往下
            if(ch-- < cl) break;
            for(int i=ch; i>=cl; i--) result.push_back(matrix[rh][i]);//固定下边界 行 从右往左
            if(rh-- < rl) break;
            for(int i=rh; i>=rl; i--) result.push_back(matrix[i][cl]);//固定左边界 列 从下往上
            if(cl++ > ch) break;
        }
        return result;
    }
};

while循环条件改了

#include 
class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        if(matrix.size() == 0 || matrix[0].size() == 0) return vector<int>();
        vector<int> result;
        int top = 0, bottom = matrix.size()-1;
        int left = 0, right = matrix[0].size()-1;
        while(top <= bottom && left <= right)
        {
            for(int i=left; i<=right; ++i) result.push_back(matrix[top][i]);//从左往右
            if(++top > bottom) break;//逐行向下 防止越界
            for(int i=top; i<=bottom; ++i) result.push_back(matrix[i][right]);//从上往下
            if(--right < left) break;
            for(int i=right; i>=left; --i) result.push_back(matrix[bottom][i]);//从右往左
            if(--bottom < top) break;
            for(int i=bottom; i>=top; --i) result.push_back(matrix[i][left]);//从下往上
            if(++left > right) break;
        }
        return result;
    }
};

10. JZ30 包含min函数的栈

题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素

两个栈

class Solution {
public:
    void push(int value) {
        if(st.empty() && minst.empty())
        {
            st.push(value);
            minst.push(value);
        }
        else
        {
            //st肯定是要入栈的 是否入minst要看值的大小
            st.push(value);
            if(value <= minst.top()) minst.push(value);
            else minst.push(minst.top());//保证minst存的是最小值且与st结构保持一致
        }
    }
    void pop() {
        st.pop();
        minst.top();
    }
    int top() {
        return st.top();
    }
    int min() {
        return minst.top();
    }
private:
    stack<int> st;
    stack<int> minst;
};

一个栈

注意一个栈时,最小值在下面

class Solution {
    //一个栈
    stack<int> st;
    int minNum = INT_MAX;
public:
    void push(int value) {
        minNum = std::min(value, minNum);//std::min不是要实现的min函数
        st.push(minNum);//最小值在下面
        st.push(value);
    }
    void pop() {
        st.pop();//弹出当前值
        st.pop();//弹出当前最小值
        //获取新的最小值 要先把新的最小值 上面的值取出
        if(!st.empty())
        {
            int temp = st.top();
            st.pop();
            minNum = st.top();
            st.push(temp);
        }
    }
    int top() {
        return st.top();
    }
    int min() {
        return minNum;
    }
};

补充内容:bitset的用法

bitset的用法

  1. bitset 介绍
    std::bitset 是 C++ 标准库中的一个类,用于表示二进制位序列。它提供了一种方便的方式来处理二进制数据,尤其适用于位运算操作。

std::bitset 类型表示一个固定长度的位序列,每个位都只能是 0 或 1。这个固定长度在创建对象时指定,并且不能在运行时更改。类似于整数类型,std::bitset 支持多种操作,包括位运算位查询位设置

  1. std::bitset 类型的创建方式:
#include 
 ​
	std::bitset<N> bitset1; // 创建一个长度为 N 的 bitset,所有位都被初始化为 0
	std::bitset<N> bitset2(value); // 使用二进制整数 value 初始化一个长度为 N 的 bitset
	std::bitset<N> bitset3(string); // 使用二进制字符串 string 初始化一个长度为 N 的 bitset
	std::bitset<N> bitset4(bitset); // 使用另一个 bitset 初始化一个长度为 N 的 bitset
	
	bitset<8> bitset2(12);  //长度为8,二进制保存,前面用0补充
	
	string s = "100101";
	bitset<10> bitset3(s);  //长度为10,前面用0补充
	
	char s2[] = "10101";
	bitset<13> bitset4(s2);  //长度为13,前面用0补充
	
	cout << bitset1 << endl;  //0000
	cout << bitset2 << endl;  //00001100
	cout << bitset3 << endl;  //0000100101
	cout << bitset4 << endl;  //0000000010101
  1. std::bitset 类型的一些常用操作:
  • size() 返回 std::bitset 的长度
  • count() 返回 std::bitset 中值为 1 的位的数量
  • any() 返回 std::bitset 中是否存在值为 1 的位
  • none() 返回 std::bitset 中是否所有位都是 0
  • all() 返回 std::bitset 中是否所有位都是 1
  • test(pos) 返回 std::bitset 中位于 pos 位置的值
  • set(pos) 将 std::bitset 中位于 pos 位置的值设为 1
  • reset(pos) 将 std::bitset 中位于 pos 位置的值设为 0
  • flip(pos) 将 std::bitset 中位于 pos 位置的值取反
  • to_ulong() 返回 std::bitset 转换成的无符号整数值
  • to_ullong() 返回 std::bitset 转换成的无符号长整数值
bitset<8> foo ("10011011");
 
cout << foo.count() << endl;  //5  (count函数用来求bitset中1的位数,foo中共有5个1
cout << foo.size() << endl;   //8  (size函数用来求bitset的大小,一共有8位

cout << foo.test(0) << endl;  //true  (test函数用来查下标处的元素是0还是1,并返回false或true,此处foo[0]为1,返回true
cout << foo.test(2) << endl;  //false  (同理,foo[2]为0,返回false
  
cout << foo.any() << endl;  //true  (any函数检查bitset中是否有1
cout << foo.none() << endl;  //false  (none函数检查bitset中是否没有1
cout << foo.all() << endl;  //false  (all函数检查bitset中是全部为1
  1. std::bitset 重载了许多二进制运算符,如 &、|、^、~ 等,使其支持类似于整数类型的位运算操作,使用左移、右移运算符进行位移操作,支持 to_string() 方法,将其转换成二进制字符串
//&、|、^、~ 等,使其支持类似于整数类型的位运算操作
 std::bitset<4> bitset1("1010");
 std::bitset<4> bitset2("0110");
 ​
 std::bitset<4> bitset3 = bitset1 & bitset2; // 按位与运算
 std::bitset<4> bitset4 = bitset1 | bitset2; // 按位或运算
 std::bitset<4> bitset5 = bitset1 ^ bitset2; // 按位异或运算
 std::bitset<4> bitset6 = ~bitset
//左移、右移运算符进行位移操作
 std::bitset<4> bitset1("0101");
 std::bitset<4> bitset2 = bitset1 << 2; // 左移 2 位,结果为 "010100"
 std::bitset<4> bitset3 = bitset1 >> 1; // 右移 1 位,结果为 "0010"
//转换成二进制字符串
 std::bitset<4> bitset1("1010");
 std::string str = bitset1.to_string(); // "1010"

你可能感兴趣的:(牛客剑指offer,链表,算法,数据结构,c++)