左神算法与数据结构——中级提升班-3

中级提升班-3

题目0返回1数量最多的行

一个矩阵,每行0在左边,1在右边,返回矩阵中1个数最多的行数

在这里插入图片描述

  • 准备list用于存放行数,ans统计最多的1的个数,从右上角开始走,左边有1则ans++,直到没有1了往下走,并记录行数,如哦下方行为0则继续往下—如图所示
void max1(vector<vector<int>> nums) {
    int i = 0;
    int j = nums[0].size() - 1;
    int ans = 1;
    int maxRes = 0;
    vector<int> res; vector<int> res1;
    while (i < nums.size() && j >= 0) {
        if (nums[i][j] == 1) {
            while (nums[i][j] == 1 && j - 1 >= 0 && nums[i][j - 1] == 1) {
                j--;
                ans++;
            }
            if (ans > maxRes) {
                maxRes = ans;
                res.clear();
                res.push_back(i);
                i++;
            }
            else if (ans == maxRes) {
                res.push_back(i);
                i++;
            }
        }
        else {
            i++;
        }
    }
    for (int i = 0; i < res.size(); i++) {
        cout << res[i] << " ";
    }
}

题目一 打包机器/洗衣机

有n个打包机器从左到右一字排开,上方有一个自动装置会抓取一批放物品到每个打
包机上,放到每个机器上的这些物品数量有多有少,由于物品数量不相同,需要工人
将每个机器上的物品进行移动从而到达物品数量相等才能打包。每个物品重量太大、
每次只能搬一个物品进行移动,为了省力,只在相邻的机器上移动。请计算在搬动最
小轮数的前提下,使每个机器上的物品数量相等。如果不能使每个机器上的物品相同,
返回-1。
例如[1,0,5]表示有3个机器,每个机器上分别有1、0、5个物品,经过这些轮后:
第一轮:1 0 <- 5 => 1 1 4
第二轮:1 <-1<- 4 => 2 1 3
第三轮:
2 1 <- 3 => 2 2 2
移动了3轮,每个机器上的物品相等,所以返回3
例如[2,2,3]表示有3个机器,每个机器上分别有2、2、3个物品,
这些物品不管怎么移动,都不能使三个机器上物品数量相等,返回-1

分成以下三类情况:

在这里插入图片描述

因此,从左开始遍历数组,每次计算以该点为i时,所对应的最少次数,数组中最大的数为所求值

int packingMachine(vector<int> arr) {
    if (arr.size() < 1) {
        return -1;
    }
    int sum = 0;
    for (int i = 0; i < arr.size(); i++) {
        sum += arr[i];
    }
    if (sum % arr.size() != 0) {
        return -1;
    }
    int ave = sum / arr.size();
    int leftSum = 0;
    int ans = 0;
    for (int i = 0; i < arr.size(); i++) {
        // 左边实际-左边所需,负数代表需要输入,正数代表需要输出
        int leftRest = leftSum - i * ave;
        // 右边实际-右边所需,负数代表需要输入,正数代表需要输出
        int rightRest = sum - leftSum - arr[i] - ave * (arr.size() - i - 1);
        if (leftRest < 0 && rightRest < 0) {
            ans = max(ans, abs(leftRest)+ abs(rightRest));
        }
        else {
            ans = max(ans, max(abs(leftRest), abs(rightRest)));
        }
        leftSum += arr[i];// 别忘记
    }
    return ans;
}

题目二zigzag

矩阵题设置边界点

用zigzag的方式打印矩阵,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
打印顺序为:0 1 4 8 5 2 3 6 9 10 7 11

  • 无需考虑单个情况,从整体开始考虑

  • 如图,设定两个边界点,一次按照斜线打印两个边界点之间的值

在这里插入图片描述

自己写的代码

void zigzag(vector<vector<int>> arr) {
	int x1 = 0, y1 = 0;
	int x2 = 0, y2 = 0;
	bool sign = true;
	while (x1 != arr.size()) {
		if (sign == true) {
			int i = x2, j = y2;
			while (j != y1 + 1) {
				cout << arr[i--][j++] << " ";
			}
		}
		else {
			int i = x1, j = y1;
			while (i != x2 + 1) {
				cout << arr[i++][j--] << " ";
			}
		}
		sign = !sign;
		if (y1 + 1 < arr[0].size()) {
			y1++;
		}
		else {
			x1++;
		}
		if (x2 + 1 < arr.size()) {
			x2++;
		}
		else {
			y2++;
		}
	}
}

左神代码

public static void printMatrixZigZag(int[][] matrix) {
		int tR = 0;
		int tC = 0;
		int dR = 0;
		int dC = 0;
		int endR = matrix.length - 1;
		int endC = matrix[0].length - 1;
		boolean fromUp = false;
		while (tR != endR + 1) {
			printLevel(matrix, tR, tC, dR, dC, fromUp);
			tR = tC == endC ? tR + 1 : tR;
			tC = tC == endC ? tC : tC + 1;
			dC = dR == endR ? dC + 1 : dC;
			dR = dR == endR ? dR : dR + 1;
			fromUp = !fromUp;
		}
		System.out.println();
	}

	public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
			boolean f) {
		if (f) {
			while (tR != dR + 1) {
				System.out.print(m[tR++][tC--] + " ");
			}
		} else {
			while (dR != tR - 1) {
				System.out.print(m[dR--][dC++] + " ");
			}
		}
	}

题目三 用螺旋方式打印矩阵

矩阵题设置边界点

用螺旋的方式打印矩阵,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
打印顺序为:0 1 2 3 7 11 10 9 8 4 5 6

  • 设置对角线为两个边界点,打印以该边界点组成的矩形的边框。
  • 最后会有三种特殊情况,需要讨论
void rotateMatrix(vector<vector<int>> arr) {
    int x1 = 0, y1 = 0;
    int x2 = arr.size() - 1, y2 = arr[0].size() - 1;
    while (x1 <= x2 && y1 <= y2) {
        if (x1 == x2) {
            for (int j = y1; j <= y2; j++) {
                cout << arr[x1][j] << " ";
            }
            break;
        }
        if (y1 == y2) {
            for (int i = x1; i <= x2; i++) {
                cout << arr[i][y1] << " ";
            }
            break;
        }
        for (int i = y1; i <= y2; i++) {
            cout << arr[x1][i] << " ";
        }
        for (int j = x1 + 1; j <= x2; j++) {
            cout << arr[j][y2] << " ";
        }
        for (int i = y2 - 1; i >= y1; i--) {
            cout << arr[x2][i] << " ";
        }
        for (int j = x2 - 1; j > x1; j--) {
            cout << arr[j][x1] << " ";
        }
        x1++;
        y1++;
        x2--;
        y2--;
    }
}

题目四 矩阵旋转

矩阵题设置边界点

给定一个正方形矩阵,只用有限几个变量,实现矩阵中每个位置的数顺时针转动
90度,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
矩阵应该被调整为:
12 8 4 0
13 9 5 1
14 10 6 2
15 11 7 3

在这里插入图片描述

void rotateMatrix(vector<vector<int>>& arr) {
    // 设置标志点,即对角线两点
    int x1 = 0, y1 = 0;
    int x2 = arr.size() - 1, y2 = arr[0].size() - 1;
    while (x1 < x2) { // 假如是奇数,最中心的也不需要交换位置
        for (int i = 0; i < x2 - x1; i++) {// 交换位置
            int temp = arr[x1][y1 + i];
            arr[x1][y1 + i] = arr[x2 - i][y1];
            arr[x2 - i][y1] = arr[x2][y2 - i];
            arr[x2][y2 - i] = arr[x1 + i][y2];
            arr[x1 + i][y2] = temp;
        }
        x1++;
        y1++;
        x2--;
        y2--;
    }
}

题目五判断aim是否在matrix中

给定一个元素为非负整数的二维数组matrix,每行和每列都是从小到大有序的。
再给定一个非负整数aim,请判断aim是否在matrix中。

在这里插入图片描述

  • 从右上角还是搜索,大了往左,小了往下,直到出边界
bool inMatrix(vector<vector<int>> arr, int val) {
    int i = 0;
    int j = arr[0].size() - 1;
    while (i <= arr.size() - 1 && j <= arr[0].size() - 1) {
        int k = arr[i][j];
        if (k > val) {
            j--;
        }
        else if (k < val) {
            i++;
        }
        else {
            cout << "存在" << endl;
            return true;
        }
    }
    cout << "不存在" << endl;
    return false;
}

题目六

贪心方法、质数判断、分解非质数

假设s和m初始化,s = “a”; m = s;
再定义两种操作,第一种操作:
m = s;
s = s + s;
第二种操作:
s = s + m;
求最小的操作步骤数,可以将s拼接到长度等于n

  • 若n为质数,则全部调操作二为最少步骤,为n - 1。若n为质数,先前s到达了k个,则进行操作一,m = k,s = 2k,则后续s必须为k的倍数,则不成立
  • n不为质数,则拆成质数的乘积,然后同理
bool isPrim(int n) { // 判断是否为质数
    if (n < 2) {
        return false;
    }
    int max = (int)sqrt((double)n);
;   for (int i = 2; i < max; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

struct ReturnType {
    int sum; // 不包括1的质数和
    int count; // 不包括1的质数个数
    ReturnType() : sum(0), count(0){}
};

ReturnType* divsSumAndCount(int n) {
    ReturnType* res = new ReturnType();
    for (int i = 2; i <= n; i++) { // 求解n由多少个质数构成
        while (n % i == 0) {
            res->sum += i;
            res->count++;
            n /= i;
        }
    }
    return res;
}

int splitNbySM(int n) {
    if (n < 2) {
        return 0;
    }
    if (isPrim(n)) {
        return n - 1;
    }
    ReturnType* res = divsSumAndCount(n);
    return res->sum - res->count;
}

题目七

小根堆自定义排序

给定一个字符串类型的数组arr,求其中出现次数最多的前K个

  • 大根堆,按照词频来排
  • 小根堆,不得超过k个,目前为止,次数最大的前k个按小根堆组织,小根堆堆顶为“门槛”
struct cmp1 {
    bool operator() (const pair<string, int>& p1, const pair<string, int>& p2) {
        return p1.second < p2.second;// 大根堆
    }
};
vector<string> topKTimes1(vector<string> arr, int k) {
    unordered_map<string, int> umap;
    vector<string> res(k);
    for (int i = 0; i < arr.size(); i++) {
        umap[arr[i]]++;
    }
    priority_queue<pair<string, int>, vector<pair<string, int>>, cmp1> que;
    for (pair<string, int> p : umap) {
        que.push(p);
    }
    for (int i = 0; i < k; i++) {
        cout << que.top().first << " ";
        res.push_back(que.top().first);
        que.pop();
    }
    return res;
}
struct cmp2 { 
    bool operator() (const pair<string, int>& p1, const pair<string, int>& p2) {
        return p1.second > p2.second;// 小根堆
    }
};
vector<string> topKTimes2(vector<string> arr, int k) {
    unordered_map<string, int> umap;
    vector<string> res(k);
    for (int i = 0; i < arr.size(); i++) {
        umap[arr[i]]++;
    }
    priority_queue<pair<string, int>, vector<pair<string, int>>, cmp2> que;
    for (pair<string, int> p : umap) {
        if (que.size() < k) {
            que.push(p);
        }
        else {
            if (que.top().second < p.second) {
                que.pop();
                que.push(p);
            }
        }
    }
    for (int i = k - 1; i >= 0; i--) {
        res[i] = que.top().first;
        cout << que.top().first << " ";
        que.pop();
    }
    return res;
}

投票并显示

自定义堆

设计一个结构,可以随时向其中加入字符串,并可以实时显示其次数top k的字符串,类似于实时投票系统

在这里插入图片描述

  • 需要以下三种结构

在这里插入图片描述

  • 词频表不断记录添加的字符串及其个数
  • 堆个数为k个,为小根堆,堆顶为“门槛”,若没满或者大于“门槛”则可以进入,每次更新堆或更新堆中的数据时,都要进行heapify操作
  • 堆位置map,记录字符串在堆的位置,若不在但出现过为-1

在这里插入图片描述

struct Node {
    string str;
    int times;
    Node(string str, int times) : str(str), times(times) {}
};

class TopKRecord {
public:
    unordered_map<string, Node*> strNodeMap;// 词频表
    vector<Node*> heap;// 小根堆(自定义堆)
    int heapSize;// 目前为止堆有多少个元素
    unordered_map<Node*, int> nodeIndexMap;// 节点所在堆位置表

    TopKRecord(int size) {// 最多实时显示size个最大值
        this->heapSize = 0;// 目前为0个节点元素
        this->heap.resize(size);
    }
    void add(string str) {
        Node* curNode = nullptr;
        int preIndex = -1;
        if (!strNodeMap.count(str)) {// 不在词频表内,表示第一次出现
            curNode = new Node(str, 1);// 建立新Node
            strNodeMap.insert({ str, curNode });// 放入词频表中
            nodeIndexMap.insert({ curNode, -1 });// 放入堆位置表中
        }
        else {// 词频表中存在
            curNode = strNodeMap[str];
            curNode->times++;
            preIndex = nodeIndexMap[curNode];
        }
        if (preIndex == -1) {// str不在小根堆中
            if (heapSize == heap.size()) {
                if (curNode->times > heap[0]->times) {// 干过了
                    nodeIndexMap[heap[0]] = -1;
                    nodeIndexMap[curNode] = 0;
                    heap[0] = curNode;
                    heapify(0, heapSize);
                }
            }
            else {// 没满直接添加进末尾,并且heapinsert
                heap[heapSize] = curNode;
                nodeIndexMap[curNode] = heapSize;
                heapInsert(heapSize++);
            }
        }
        else {// str在小根堆中
            heapify(preIndex, heapSize);
        }
    }
private:
    void heapify(int preIndex, int heapSize) {
        int left = preIndex * 2 + 1;
        int right = preIndex * 2 + 2;
        while (left < heapSize) {
            int largest = (right < heapSize && heap[right]->times > heap[left]->times) ? right : left;
            largest = heap[largest]->times > heap[preIndex]->times ? largest : preIndex;
            if (largest == preIndex) {
                break;
            }
            swap(preIndex, largest);
            preIndex = largest;
            left = preIndex * 2 + 1;
            right = preIndex * 2 + 2;
        }
    }
    void heapInsert(int index) {
        while ((index - 1) / 2 >= 0 && heap[index]->times < heap[(index - 1) / 2]->times) {
            swap(index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
    void swap(int preIndex, int largest) {
        nodeIndexMap[heap[preIndex]] = largest;
        nodeIndexMap[heap[largest]] = preIndex;
        Node* tmp = heap[preIndex];
        heap[preIndex] = heap[largest];
        heap[largest] = tmp;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值