习题笔记(一)

习题笔记

1. 技巧题

1. 消失的数字(按位与)

原题链接
在这里插入图片描述

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n = nums.size();
        int ans = 0;
        for(int i = 1; i <= n; ++i){
            ans ^= i; // [0, n]异或结果
            ans ^= nums[i - 1]; // [0, n]异或结果与nums异或结果
        }
        return ans;
    }
};

2.穷举法 7-1 用扑克牌计算24点 (25 分)

原题链接

注意:

  1. 固定位置用数组遍历
  2. 俩俩运算用函数实现
#include<bits/stdc++.h>
using namespace std;
double get(double a,char c,double b)//两个数之间的运算 
{
	switch(c)
	{
		case '+':return a+b;break;
		case '-':return a-b;break;
		case '*':return a*b;break;
		case '/':return a/b;break;
	}
}
int main()
{
	int i,j;
	int temp=1;
	int a,b,c,d,e,f;
	float num[4];
	char cha[4]={'+','-','*','/'};
	cin>>num[0]>>num[1]>>num[2]>>num[3];
	for(i=0;i<=3&&temp;i++)
	for(j=0;j<=3&&temp;j++)
	for(a=0;a<=3&&temp;a++)
	for(b=0;b<=3&&temp;b++)
	for(c=0;c<=3&&temp;c++)
	for(e=0;e<=3&&temp;e++)
	for(f=0;f<=3&&temp;f++)
	{
		if(i==a||i==c||i==f||a==c||a==f||f==c)
		continue;
		        if(temp&&get(get(get(num[i],cha[j],num[a]),cha[b],num[c]),cha[e],num[f])==24.0)//1.通过两两运算 :(((a先与b)再与c)最后与d)
				{
					temp=0;
					printf("((%.0lf%c%.0lf)%c%.0lf)%c%.0lf\n",num[i],cha[j],num[a],cha[b],num[c],cha[e],num[f]); 
//					cout<<"   i = "<<i<<"  a ="<<a<<"    c  =  "<<c<<"  f =  "<<f<<endl;
				}
				if(temp&&get(get(num[i],cha[j],num[a]),cha[b],get(num[c],cha[e],num[f]))==24.0)//2.通过两两运算 :((a先与b)再与(c先与d))
				{
					temp=0;
					printf("(%.0lf%c%.0lf)%c(%.0lf%c%.0lf)\n",num[i],cha[j],num[a],cha[b],num[c],cha[e],num[f]);
//					cout<<"   i = "<<i<<"  a ="<<a<<"    c  =  "<<c<<"  f =  "<<f<<endl;
				}
				if(temp&&(get(num[i],cha[j],get(num[a],cha[b],num[c])),cha[e],num[f])==24.0)//3.通过两两运算 :((a再与(b先与c))最后于d)
				{
					temp=0;
					printf("(%.0lf%c(%.0lf%c%.0lf))%c%.0lf\n",num[i],cha[j],num[a],cha[b],num[c],cha[e],cha[e]);
//					cout<<"   i = "<<i<<"  a ="<<a<<"    c  =  "<<c<<"  f =  "<<f<<endl;
				}
				if(temp&&get(num[i],cha[e],get(get(num[a],cha[b],num[c]),cha[j],num[f]))==24.0)//4.通过两两运算 :(a最后与((b先与c)再与d))
				{
					temp=0;
					printf("%.0lf%c((%.0lf%c%.0lf)%c%.0lf)\n",num[i],cha[e],num[a],cha[b],num[c],cha[j],num[f]);
//					cout<<"   i = "<<i<<"  a ="<<a<<"    c  =  "<<c<<"  f =  "<<f<<endl;
				}
				if(temp&&get(num[i],cha[j],get(num[a],cha[b],get(num[c],cha[e],num[f])))==24.0)//5.通过两两运算 :(a最后与(b再与(c先与d)))
				{
					temp=0;
					printf("%.0lf%c(%.0lf%c(%.0lf%c%.0lf))\n",num[i],cha[j],num[a],cha[b],num[c],cha[e],num[f]);
//					cout<<"   i = "<<i<<"  a ="<<a<<"    c  =  "<<c<<"  f =  "<<f<<endl;
				}
	}
	if(temp==1)
	cout<<"-1"<<endl;
 } 

2. 常规题

1. N个分数求和 (20 分)

7-1 N个数求和 (20 分)

递归最大公因数

long long gcd(long long a, long long b)
{
	if (b == 0)
		return a;
	else 
		return gcd(b, a % b);
}

分数用结构体读入,二维数组也可以

struct fenshu
{
	long long fenzi;
	long long fenmu;
}a[100];

2. 二分法求多项式单根

二分法求多项式单根

函数功能的实现可以用 函数

double f(double x)
{
    return a3 * x * x * x + a2 * x * x + a1 * x + a0;
}

阈值——保留2为小数

 while (b - a >= 0.01)

3.喝汽水问题

喝汽水

处理空瓶子

while(temp >= 2{
	 sum += temp / 2;
	 temp = temp / 2 + temp % 2; 
}

如果剩余一个空瓶,再借一瓶吗,问面试官

4. 双指针 排奇偶数组

调整奇数偶数顺序

while中while

5. 谁是凶手(逻辑推理)

比特 谁是凶手

字符遍历

for (killer = 'a'; killer <= 'd'; killer++)

说真话,意味着表达式的值为1

 if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)

每个人说对了一半

(p[1] == 2) + (p[0] == 3) == 1

6. 猜名次(逻辑推理)

通过递归,模拟n次的n次循环

for (p[n] = 1; p[n] <= 5; p[n]++)
	{
		diveRank(p, n + 1); /*通过递归模拟多层循环,
		每进一次递归相当于进了一层新的循环。*/
	}

判断谁说的对,把所有情况罗列后,带入判断条件中

for (p[0] = 1; p[0] <= 5; p[0]++)
	{
		for (p[1] = 1; p[1] <= 5; p[1]++)
		{
			for (p[2] = 1; p[2] <= 5; p[2]++)
			{
				for (p[3] = 1; p[3] <= 5; p[3]++)
				{
					for (p[4] = 1; p[4] <= 5; p[4]++) //五层循环遍历
					{
						//这里是五个人的描述,由于比较表达式只有0和1两个结果,如果要两个条件有且只有一个为真,则可以用比较表达式的值总和为1的方式直接判定。别忘了还要判定不能并列。
						if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
							(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
							(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
							(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
							(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
							checkData(p) //不能并列
							)
						{
							for (int i = 0; i < 5; i++)
							{
								printf("%d ", p[i]);
							}
							putchar('\n');
						}
					}
				}
			}
		}
	}

7. 公务员面试

原题链接

按集合 的持续输入

int val;
    while(cin >> val)
    {
        int a[8];
        a[0]=val;
        for(int i=1;i<7;++i)

先读入一个用于判断循环,然后在大循环里读入集合-1的个数。

8. 二维数组 限制打印

把两个元素假装成一个数组元素

空心三角形图案

for(int i1 = 0; i1 < j - 2; i1++)
    {
        for(int j1 = 0; j1 < j -2; j1++)
        {
            if( ((i1 + j1 <= j-2-1)&&(i1<=j1)) || ((j1+i1 >=j-2-1)&&(i1>=j1)) )
                cout << q;
            else if((i1 + j1 <= j-2-1)&&(i1>j1))
                cout << ' ';
        }

9. 阅览室

原题链接

10. 素数对(6*k ±1)

原题链接

11. 装箱问题 (20 分)

二重循环

把箱子当成数组,但是箱子的数目得比物品多

12. 调整奇数偶数顺序

left < right 防止数组左边越界

原题链接

void swap_arr(int arr[], int sz)
{
	int left = 0;
	int right = sz-1;
	int tmp = 0;
 
 
	while(left<right)
	{
     // 从前往后,找到一个偶数,找到后停止
		while((left<right)&&(arr[left]%2==1))
		{
			left++;
		}
     
		// 从后往前找,找一个奇数,找到后停止
		while((left<right)&& (arr[right]%2==0))
		{
			right--;
		}
     
     // 如果偶数和奇数都找到,交换这两个数据的位置
     // 然后继续找,直到两个指针相遇
		if(left<right)
		{
			tmp = arr[left];
			arr[left] = arr[right];
			arr[right] = tmp;
		}
	}
}

13. 在递增矩阵中搜索数据

	while (j >= 0 && i < y)
	{
		if (a[i][j] < f) //比我大就向下
		{
			i++;
		}
		else if (a[i][j] > f) //比我小就向左
		{
			j--;
		}
		else
		{
			return 1;
		}
	}

14.输出整数各位数字(/和%)

原题链接

/ 和 %(10的几位数)就是留下前或后的几位数

15. 黑洞数(数组大小重排)

原题链接

16. 字符串strstr 判断子串

原题链接

翻转情况的罗列,就是把原数组原地扩2倍

17. 合并两个数组(把一个数组放在另一个数组)

在这里插入图片描述
在这里插入图片描述

3. 算法题

1. 分治算法求最大子列和

分治算法求最大子列和

分治法步骤

  1. 定义数据
  2. 递归结束条件
  3. 分,然后根据分的情况,递归(递归的接受值,也就是递归函数的返回值,也是目的值)
  4. 治的过程
  5. 返回目的值

2. x^N的时间复杂度为(logN)的算法

答案

奇偶次幂

奇次幂 用ret 次幂-1
偶次幂 x*x 次幂 /2

3. 递归斐波那契的时间复杂度

在这里插入图片描述

4. 分离两个数的异或值)数组中数字出现的次数 II

原题链接

mask=xor&(-xor) 得到一个数二进制最低的1的值

分两组,一组最低的1为0处理

for(int i=0;i<numsSize;i++){
        if((nums[i]&mask)==mask)
            ans[0]^=nums[i];
        else
            ans[1]^=nums[i];

5. 出现三次数中找出现一次的

思路:位运算

数组按位相加(两重循环)

for(int i = 31; i >= 0; i--)
    {
        for(int j = 0; j < numsSize; j++)
        {
            bit += nums[j] >> i & 1;
        }
        ans = ans * 2 + bit % 3;
        bit = 0;
    }

由位值,求总值

6. 数组元素循环右移问题

直接输出法

本质交换法

ans = ans * 2 + bit % 3;

顺序表

1. 要画图做题,删除有序数组中的重复项

原题链接

方法一:const修饰两个指针,用于保存数组值,还可以 指坐标
int removeDuplicates(int* nums, int numsSize){
    const int *p = (const int *)nums;
    const int *q = (const int *)nums;
    int size = 0;

    if (numsSize > 0) {
        size = 1;
    }

    //p指针为输入数组中不重复值的指针,q指针为变化指针
    for (int i = 0; i < numsSize; i++, q++) {
        if (*p != *q) {
            p = q;
            nums[size++] = *q;
        }
    }
    return size;
}
方法二: 双指针,后一个数与前一个数比,重复一定被换
int removeDuplicates(int* nums, int numsSize) {
    if (numsSize == 0) {
        return 0;
    }
    int fast = 1, slow = 1;
    while (fast < numsSize) {
        if (nums[fast] != nums[fast - 1]) {
            nums[slow] = nums[fast];
            ++slow;
        }
        ++fast;
    }
    return slow;
}
方法三:三指针,

在这里插入图片描述

2. 数组整数+一个数

原题链接
在这里插入图片描述

数组的扩容(加法最多 多出一位)

在这里插入图片描述

链表

1. 移除链表元素

原题链接

注意:

  1. 如果头节点是空 直接用while循环判断就好了
  2. 如果头节点就是需要删除的
  3. 删除的时候,需要保留上一个点的指针,所以需要再创建一个指针

2. 206. 反转链表

原题链接

迭代(双指针+一个指针用于指向下一个)
递归
递归就是先找到最后一个
接下来的操作,就是对每个最后一个的操作
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *tail = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return tail;
    }
};
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* prev = NULL;
    struct ListNode* curr = head;
    while (curr) {
        struct ListNode* next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

3. 876. 链表的中间结点

原题链接

快慢结点 循环判断条件的设定(假设法)

假设有3个节点
假设有2个节点
此两种情况下,指针是否循环,便是判断条件

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

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

原题链接

注意

  1. k的值<=0 或者大于结点数
普通解法(遍历两次)
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (!pListHead || k <= 0) return nullptr;
        int n = 0;
        ListNode *cur = pListHead;
        while (cur) {
            cur = cur->next;
            ++n;
        }
        if (n < k) return nullptr;
        n -= k;
        while (n--) {
            pListHead = pListHead->next;
        }
        return pListHead;

    }
};
快慢指针
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (!pListHead || k <= 0) return nullptr;
        auto slow = pListHead, fast = pListHead;

        while (k--) {
            if (fast)
                fast = fast->next;
            else 
                return nullptr; //如果单链表长度 < K,直接返回
        }
        while (fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

5. 21. 合并两个有序链表

原题链接
在这里插入图片描述

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* preHead = new ListNode(-1);

        ListNode* prev = preHead;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                prev->next = l1;
                l1 = l1->next;
            } else {
                prev->next = l2;
                l2 = l2->next;
            }
            prev = prev->next;
        }

        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev->next = l1 == nullptr ? l2 : l1;

        return preHead->next;
    }
};

6. 链表分割

原题链接

注意:

  1. 这个链表不是结构体指针,就是结构体
  2. 把小于X的链接一个链表,另一个是另一个链表,然后再连接
public class Partition {
    public ListNode partition(ListNode pHead, int x) {
        ListNode cur = pHead;
        ListNode bs = null;
        ListNode be = null;
        ListNode as = null;
        ListNode ae = null;
        
        while(cur != null){
            if(cur.val < x){
                if(bs == null){
                    bs = cur;
                    be = cur;
                }else{
                    be.next = cur;
                    be = be.next;
                }
            }else{
                if(as == null){
                    as = cur;
                    ae = cur;
                }else{
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        if(bs == null){
            return as;
        }
        be.next = as;
        if(as != null){
            ae.next = null;
        }
        return bs;
    }
}

7. 判断链表是否为回文结构

原题链接

方法一:把链表放置到数组中
方法二:用快慢指针取链表中间位置,然后再从中间位置开始遍历判断

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) 
    {
        struct ListNode*slow=A;
        struct ListNode*fast=A;
        while(fast->next)
        {
            slow=slow->next;
            fast=fast->next;
        }
        struct ListNode*change=NULL;
        struct ListNode*head=slow;
        while(head)
        {
            struct ListNode*next=slow->next;
            head->next=change;
            change=head;//重新置头
            head=next;
        }
        while(slow)
        {
            if(A->val!=change->val)
            {
                return false;
            }
            slow=slow->next;
            change=change->next;
        }
        return true;
    }
};

8. 相交链表(双指针)

原题链接

注意:

  1. 首先判断指针是否为空
  2. 当一个链表遍历完成后,让它为另一个指针的头,继续判断等待另一个链表遍历完成后,成为另一个头,此时两个链表相差的节点数就被抹平了。然后再次遍历,如果还没有相等的,那么就会返回两个链表的末尾的NULL
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL) {
        return NULL;
    }
    struct ListNode *pA = headA, *pB = headB;
    while (pA != pB) {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    }
    return pA;
}
先走相差的个数

在这里插入图片描述

9. 判断循环链表

原题链接

bool hasCycle(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return false;
    }
    struct ListNode* slow = head;
    struct ListNode* fast = head->next;
    while (slow != fast) {
        if (fast == NULL || fast->next == NULL) {
            return false;
        }
        slow = slow->next;
        fast = fast->next->next;
    }
    return true;
}

10. 返回循环链表中的,入环位置

入环位置,和快慢指针的相遇位置是有关系的
struct ListNode* detectCycle(struct ListNode* head) {
    struct ListNode *slow = head, *fast = head;
    while (fast != NULL) {
        slow = slow->next;
        if (fast->next == NULL) {
            return NULL;
        }
        fast = fast->next->next;
        if (fast == slow) {
            struct ListNode* ptr = head;
            while (ptr != slow) {
                ptr = ptr->next;
                slow = slow->next;
            }
            return ptr;
        }
    }
    return NULL;
}

11. 复制带随机指针的链表

原题链接

注意:

  1. 因为复制节点,random所指向的节点,也应该是复制而来的,所以如果在第一个就直接去找random,是不行的,起码要全部复制出来后,再去处理random。
  2. 所以复制节点链表,需要技巧,就是先复制到每个原节点的后面,然后就可以用random- >next->next找到复制节点

在这里插入图片描述

12. 对链表进行插入排序

原题连接

注意:

  1. 遍历的时候,注意需要保留什么指针,增加什么指针
  2. 结构体也是指针

在这里插入图片描述

13. 删除链表中重复的结点

原题链接

注意:

  1. 删除节点,在题中就直接把指针跳过这个结点就好了,而不是真的free掉
  2. 极端情况的讨论,便是都是成对出现的,都要删除,那么辅助指针的个数就会更加明确

在这里插入图片描述

14. 删除 链表中给定结点地址

原题链接

直接改变自身的地址,那么上个指针的也对相应就变了

15. 两个有序链表序列的交集

两个有序链表序列的交集

原题链接

typedef 对指针
typedef struct node {
    int data;
    struct node* next;
}*LinkList;
链表的读取,需要一个指针记录头,一个记录读取,一个记录下一个
LinkList createList() {  //尾插法建立链表
    LinkList H = (LinkList)malloc(sizeof(struct node));
    LinkList p = H;
    int ch;
    scanf("%d", &ch);
    while (ch != -1) {
        LinkList q = (LinkList)malloc(sizeof(struct node));
        q->data = ch;
        p->next = q;
        p = q;
        scanf("%d", &ch);
    }
    p->next = NULL;
    return H->next;

链表结构体的新开辟

1.用函数 listnode* buylistnode(int x)
2.结构体构造函数

4. PTA 例题

1. 两个有序序列的中位数 (25 分)

原题链接

动态数组的开辟(new,malloc)

    int *s1 = new int[n];
    int *s2 = new int[n];
	样例:
	int* a = (int*)malloc(sizeof(int) * n);
    int* a = new int[n];

双指针算法(用于 两个数组比较)

        for( i = 0, j = 0; m != 0;)
        {
            if(s1[i]<=s2[j])
            {
                i++,m--;
            }
            else
            {
                j++,m--;
            }
        }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值