Young氏矩阵

一个 m x n 的Young氏矩阵是指,每一行数据都是从左到右排好序,每一列的数据也都是从上到下排好序。其中也可能存在一些INF的数据,表示不存在的元素,一个mxn的Young氏矩阵最多用来存放 r <= mn个元素。

详细见《算导》P.83

Young氏矩阵类似于堆的结构,主要实现的API包括以下:

1. void insert(int x)

功能:将一个元素x插入到矩阵中,复杂度O(m+n)

算法过程:

1) 判断矩阵是否为Full

2) 如果不为Full,插入元素到矩阵的右下角(row, col)位置,然后进行swim(row, col)将元素移动到合适位置。

2. int getMin()

功能:返回矩阵中的最小值,复杂度O(1)

算法过程:

1) 判断矩阵是否为Empty

2) 直接返回mat[0][0].左上角元素,依据矩阵的性质

3. int delMin()

功能:返回矩阵中的最小值,并把它从矩阵中删除,复杂度O(m+n)

算法过程:

1) 判断矩阵是否为Empty

2) 暂存mat[0][0]元素用于返回,把矩阵最右下角的元素放到mat[0][0],使用sink(0, 0)进行下沉调整元素到合适位置

4. bool seach(int x)

功能:判断矩阵是否存在元素x,复杂度O(m+n)

算法过程:

版本1:通过递归的方式,比较当前mat[x][y] 和 key的关系,将划分到是否需要在(m-1)x(n)子矩阵和mx(n-1)子矩阵进行递归查找

版本2:初始位置为矩阵右上角,如果当前元素大于key,向左移动,如果当前元素小于key向下移动。

5. void sort()

功能:对矩阵元素进行排序,对矩阵执行元素个数次的delMin()操作就可以得到排序结果。复杂度O(n*m*(n+m))

辅助函数:

==两个函数的实现与实现堆的swim和sink操作思想完全一样==

void swim(int i, int j)

功能:对(i, j)位置元素进行上浮操作,与(i-1, j) 和 (i, j-1)位置的元素进行比较,与他们之间的最大值进行交换

void sink(int i, int j)

功能:对(i, j)位置元素进行下沉操作,与(i+1, j) 和 (i, j+1)位置的元素进行比较, 与他们之间的最小值进行交换

完整代码如下:

const int INF = 0x3fffffff;

class YoungMatrix {
public:
    YoungMatrix(int row, int col); // constructor
    ~YoungMatrix(); // destructor

    void insert(int x); // insert a element
    int delMin(); // delete and return the minimal element
    bool search(int x, int version = 1); // search a element
    int getMin(); // return the minimal element
    // sort(); // 调用 n*n delMin()得到结果n*n*(n+n) O(n^3)

    // print the matrix
    void printMatrix() {
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++)
                cout << mat[i][j] << "\t";
            cout << endl;
        }
        cout << endl;
    }

    /*** auxiliary function ***/
private:
    void swim(int i, int j); // swim the element at (i, j), 从右下角上升到左上角
    void sink(int i, int j); // sink the element at (i, j), 从左上角下沉到右下角
    bool searchHelp1(int x, int y, int key); // recursive function to search the element, divide the problem into sub-matrix (m, n-1) and (m-1, n)
    bool searchHelp2(int key); // init the position in (0, col-1). like go down the stairs from right to left

private:
    int **mat;
    int row, col;
    int num;
};

YoungMatrix::YoungMatrix(int row, int col) {
    this->num = 0;
    this->row = row;
    this->col = col;
    mat = new int*[row];
    for (int i = 0; i < row; i++)
        mat[i] = new int[col];
    for (int i = 0; i < row; i++)
    for (int j = 0; j < col; j++)
        mat[i][j] = INF;
}

YoungMatrix::~YoungMatrix() {
    for (int i = 0; i < row; i++)
        delete[] mat[i];
    delete[] mat;
}

void YoungMatrix::swim(int i, int j) {
    while (true) {
        int v = mat[i][j], p = -1;
        if (i - 1 >= 0 && v < mat[i - 1][j]) { v = mat[i - 1][j], p = 0; }
        if (j - 1 >= 0 && v < mat[i][j - 1]) { v = mat[i][j - 1]; p = 1; }

        if (p == -1) break;

        if (p == 0) {
            swap(mat[i - 1][j], mat[i][j]);
            i -= 1;
        }
        else {
            swap(mat[i][j - 1], mat[i][j]);
            j -= 1;
        }
    }
}

void YoungMatrix::sink(int i, int j) {
    while (true) {
        int v = mat[i][j], p = -1;
        if (i + 1 < row && v > mat[i + 1][j]) { v = mat[i + 1][j], p = 0; }
        if (j + 1 < col && v > mat[i][j + 1]) { v = mat[i][j + 1]; p = 1; }

        if (p == -1) break;

        if (p == 0) {
            swap(mat[i + 1][j], mat[i][j]);
            i += 1;
        }
        else {
            swap(mat[i][j + 1], mat[i][j]);
            j += 1;
        }
    }
}

void YoungMatrix::insert(int x) {
    // is full?
    if (num == col * row) {
        cerr << "Error: the matrix is full" << endl;
        return;
    }

    // put at the last position
    this->num++;

    int i = row - 1, j = col - 1;
    mat[i][j] = x;
    swim(i, j);
}

int YoungMatrix::getMin() {
    if (this->num > 0) return mat[0][0];
    else {
        cerr << "Error: The matrix is empty" << endl;
        return -1;
    }
}

int YoungMatrix::delMin() {
    if (this->num <= 0) {
        cerr << "Error: The matrix is empty" << endl;
        return -1;
    } else {
        int ret = mat[0][0];
        mat[0][0] = mat[row - 1][col - 1];
        this->num--;
        sink(0, 0);
        return ret;
    }
}

bool YoungMatrix::search(int x, int version) {
    if (this->num <= 0) return false;
    if (version == 1) {
        return searchHelp1(0, 0, x);
    } else {
        return searchHelp2(x);
    }
}

bool YoungMatrix::searchHelp1(int x, int y, int key) {
    if (x >= row || y >= col) return false;
    if (mat[x][y] < key) return searchHelp1(x + 1, y, key) || searchHelp1(x, y + 1, key);
    else if (mat[x][y] > key) return false;
    else return true;
}

bool YoungMatrix::searchHelp2(int key) {
    int i = 0, j = col - 1;
    while (true) {
        if (i >= row || j >= col) return false;
        if (mat[i][j] == key) return true;
        else if (mat[i][j] < key) i++;
        else if (mat[i][j] > key) j--;
    }
}

测试代码:

#include "YoungMatrix.h"
using namespace std;

int a[] = { 9, 16, 3, 2, 4, 8, 5, 14, 12 };

int main(int argc, char** argv) {
    YoungMatrix ym(4,4);
    int e;
    for (int i = 0; i < 9; i++) {
        ym.insert(a[i]);
    }
    ym.printMatrix();
    cout << "search result: " << ym.search(2, 2) << endl;
    cout << ym.delMin() << endl;
    cout << "search result: " << ym.search(2, 2) << endl;
     ym.printMatrix();
    return 0;
}
时间: 2024-12-16 21:14:26

Young氏矩阵的相关文章

算法导论 6-3 Young氏矩阵

一.题目 二.思考 最小Young氏矩阵和最小堆的思想差不多,可以通过比较两者的同异来理解Young氏矩阵 不同点:   min-Heap min-Young 堆顶(最小值) H[1] Y[i][j] 最后一个元素的位置 H[N] Y[N][N] 最后一个元素 不一定是最大值 一定是最大值 parent H[i]的parent是H[i/2] Y[i][j]的parent是Y[i-1][j]和Y[i][j-1] child H[i]的child是H[i*2]和H[i*2+1] Y[i][j]的ch

算法导论 第六章 思考题6-3 Young氏矩阵

这题利用二叉堆维持堆性质的办法来维持Young氏矩阵的性质,题目提示中写得很清楚,不过确实容易转不过弯来. a,b两问很简单.直接看c小问: 按照Young氏矩阵的性质,最小值肯定在左上角取得,问题在于取出最小值后如何保持矩阵的性质.可以参照max_heapify中的做法,先取出最小值,然后将矩阵左上角置为最大值,这样左上角处的元素必然导致Young氏矩阵的性质违背,于是考虑该元素右边的元素和该元素下边的元素,问题是该与右边元素交换还是与下边元素交换呢?可以发现,如果与T(右)和T(下)中较小的

Young氏矩阵类C++实现代码 算法导论6.3

个人总结: 1.int **p和 int a[M][N]之间的区别: 1) int **指向指针的指针:而后者的类型是数组名,类型为 int (*)[N],即指向的是整个一行. 2) (a+1) 表示地址增加M*sizeof(int),需要注意的一点是a[i]是第i行开头的地址,&a和a的值是一样的.数组名是附带大小属性的,而指针是一个存储了地址的变量.特意去看了一下声明数组的汇编代码,其中一条指令是mov %gs:0x14,%eax  (数组大小20即0x14),最后也貌似也会检查一下数组是否

Young氏矩阵实现代码(C++) 算法导论 6.3

个人总结: 1.int **p和 int a[M][N]之间的区别: 1) int **指向指针的指针:而后者的类型是数组名,类型为 int (*)[N],即指向的是整个一行. 2) (a+1) 地址增加M*sizeof(int),需要注意的一点是a[i]是第i行开头的地址,&a和a的值是一样的.数组是有大小的,而指针是一个存储了地址的变量.特意去看了一下声明数组的汇编代码,其中一条指令是 mov %gs:0x14,%eax (数组大小20即0x14),最后也貌似也会检查一下数组是否溢出,溢出的

程序员修炼之路

0.前言:在路上,再上路 在前言<程序员修炼之路-(0)前言:在路上, 再上路>中已经对知识体系进行了梳理,主要分为问题定义.算法与数据结构.系统平台API.编程语言实现.代码设计.测试验证等等.目前集中精力编写算法与数据结构部分,这可能也是最艰难的一部分.其他部分之前零零散散有所涉及,之后再慢慢补充. 1.计算机数学 离散数学与具体数学. 2.算法分析设计 对于这一部分的内容编排,主要以<算法设计与分析基础>.<算法>以及LeetCode算法题中的问题分类为横向,以&

十一、从头到尾彻底解析Hash 表算法

在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  出处:http://blog.csdn.net/v_JULY_v.  说明:本文分为三部分内容,    第一部分为一道百度面试题Top K算法的详解:第二部分为关于Hash表算法的详细阐述:第三部分为打造一个最快的Hash表算法.----------------------------------

从头到尾彻底解析Hash 表算法

在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  出处:http://blog.csdn.net/v_JULY_v.  说明:本文分为三部分内容,    第一部分为一道百度面试题Top K算法的详解:第二部分为关于Hash表算法的详细阐述:第三部分为打造一个最快的Hash表算法.----------------------------------

第6章 堆排序

#include<stdio.h> #include<stdlib.h> #include<string.h> #define leftChild(i) (2*(i)+1) //交换 void swap(int *a, int i, int j) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } //堆下溯 void maxHeapify(int *a, int i, int n) { int child, tmp; for (t

Robotics Lab3 ——图像特征匹配、跟踪与相机运动估计

Robotics Lab3 --图像特征匹配.跟踪与相机运动估计 图像特征匹配 图像特征点 携带摄像头的机器人在运动过程中,会连续性地获取多帧图像,辅助其感知周围环境和自身运动.时间序列上相连的两幅或多幅图像,通常存在相同的景物,只是它们在图像中的位置不同.而位置的变换恰恰暗含了相机的运动,这时就需要相邻图像间的相似性匹配. 选取一大块图像区域进行运动估计是不可取的.已知图像在计算机内部是以数字矩阵的形式存储的,[如灰度图的每个元素代表了单个像素的灰度值].而对于点的提取和匹配较为方便,且和数字