剑指offer C++ 第二章 2.3数据结构

01 二维数组

bool Find(int* matrix, int rows, int columns, int number) 
// 这里传入数组 (int*)matrix 或 &matrix[0][0]
{
    bool found = false;
    // 确保数组不为空,nullptr!!C++定义的空指针
    if (matrix != nullptr && rows > 0 && columns > 0)
    {
        // 从右上角开始搜索,初始化行、列
        int row = 0;
        int column = columns - 1;//
        while (row < rows && column >= 0)
        {
         // 步骤 1:右上角数字为所寻找的数字
            if (matrix[row * columns + column] == number)
            {
                cout<< matrix[row * columns + column]<<endl;
                found = true;//标识为true
                break;
            }
         // 步骤 2:右上角数字大于所寻找数字,可以排除右上角数字所在的列
         //因为列下面都是比该数字大的数字
            else if (matrix[row * columns + column] > number)
                --column;
         // 步骤 3:右上角数字大于所寻找数字,可以排除右上角数字所在的行
         //因为该数字左侧,即行所在数字都是比该数字小的数字
            else
                ++row;
        }
    }
    return found;//找到,found=true;否则为false。
}
//从左下角比较
bool Find(int target, vector<vector<int>> array) {
        int rows = array.size();
        int cols = array[0].size();
        bool isFound = false;
        if(rows > 0 && cols > 0)
        {
            int row = rows-1, col = 0;
            while(row>=0&&col<cols)
            {
                int cmp = array[row][col];  
                if(cmp == target)
                {
                    isFound=true;
                    cout<<"target position: row "<<row<<",column "<<col<<endl;
                    break;
                }
                else if(cmp>target)
                {
                    --row;
                }
                else
                {
                    ++col;  
                }
                
            } 
        }
        return isFound;

}

字符串

会把常量字符串放到单独的一个内存区域中,当几个指针赋值给相同的常量字符串时,它们实际上会指向相同的内存地址

但如果用常量内存初始化数组,数组的地址是不相同的。

02 替换空格

#include<iostream>
using namespace std;

// 替换空格,length是字符数组string的总容量
void ReplaceBlank(char string[], int length){
    if (string == NULL || length <= 0){
        return;
    }
    // 字符串string的实际长度
    int originLength = 0;
    int numberOfBlank = 0;
    int i = 0;
    // 统计空格的数量和字符的数量
    while (string[i] != '\0'){
        ++originLength;//统计字符的数量
        if (string[i] == ' ')
            ++numberOfBlank;//统计空格数量
        ++i;
    }
    // 替换空格后的字符串新长度——一个空格用%20代替,增加了两个长度
    int newLength = originLength + numberOfBlank * 2;
    if (newLength > length)
        return;
/*替换空格操作!!
——两个指针,一个指向原始字符串的结尾,一个在新的字符串的结尾位置
*/
    int indexOfOriginal = originLength;//原始字符串长度
    int indexOfNew = newLength;//新的字符串长度
    while (indexOfOriginal >= 0 && indexOfNew > indexOfOriginal){
        if (string[indexOfOriginal] == ' '){
            string[indexOfNew--] = '0';
            string[indexOfNew--] = '2';
            string[indexOfNew--] = '%';
        }
        else{
            string[indexOfNew--] = string[indexOfOriginal];
        }
        indexOfOriginal--;
    }
}

// 测试
int main(void){
    char str[10] = " hello";
    char str2[10] = "he llo";
    char str3[10] = "hello ";
    char str4[30] = "We  are  happy.";
    char str5[30] = "helloWorld.";
    char *str6 = NULL;
    char str7[10] = "";
    char str8[10] = " ";
    char str9[20] = "     ";
    // 空格位于字符串的最前面
    cout << "origin: " << str;
    ReplaceBlank(str, 10);
    cout << ",--> " << str << endl;
    // 空格位于字符串的最后面
    cout << "origin: " << str2;
    ReplaceBlank(str2, 10);
    cout << ",--> " << str2 << endl;
    // 空格位于字符串的中间
    cout << "origin: " << str3;
    ReplaceBlank(str3, 10);
    cout << ",--> " << str3 << endl;
    // 字符串中有多个连续空格
    cout << "origin: " << str4;
    ReplaceBlank(str4, 30);
    cout << ",--> " << str4 << endl;
    // 输入的字符串没有空格
    cout << "origin: " << str5;
    ReplaceBlank(str5, 30);
    cout << ",--> " << str5 << endl;
    // 字符串是空指针
    ReplaceBlank(str6, 10);
    // 字符串是空字符串
    cout << "origin: " << str7;
    ReplaceBlank(str7, 10);
    cout << ",--> " << str7 << endl;
    // 字符串只有一个空格字符
    cout << "origin: " << str8;
    ReplaceBlank(str8, 10);
    cout << ",--> " << str8 << endl;
    // 字符串中只有多个连续空格
    cout << "origin: " << str9;
    ReplaceBlank(str9, 20);
    cout << ",--> " << str9 << endl;

    system("pause");
    return 0;
}

链表
链表的简单使用
假设我们要构造一个int类型的链表,那么一个节点中就需要包含两个元素:

1.一个是当前节点所保存的值,设为int value。
2.另一个就是 指向下一个节点的指针,我们再假设这个节点类是node,那么这个指针就是 node *next。

这里一定不是int *next。因为这个指针指向的下一个元素是一个类的实例,而不是int类型的数据。那么node这个类最简单的实现就如下

struct ListNode{
            int value;
            ListNode *next;
} 

在链表末尾添加一个结点代码:

void AddToTail(ListNode **pHead,int value){
//pHead是一个指向指针的指针
          ListNode *pNew=new ListNode();
          pNew->value=value;
          pNew->next=nullptr;//生成一个新的结点
          if(*pHead==nullptr){//若为最后一个结点,则直接添加
          *pHead=pNew;
          } else{
            ListNode* pNode = *pHead;//再创造一个指针
	        while(pNode->next != nullptr)
	        //只要该指针的next!=null,指针指向下一个结点
	            pNode = pNode->next;
	        pNode->next = pNew;//当指针next=null,则将生成的结点加入
    }

    return;
}


// 删除某个值为value的结点
void RemoveNode(ListNode** pHead, int value){
    if(pHead == nullptr || *pHead == nullptr) return;
    ListNode* pToDeleted = nullptr;
    if((*pHead)->val == value){
        pToDeleted = *pHead;
        *pHead = (*pHead)->next;
    }else{
        ListNode* pNode = *pHead;
        while(pNode->next != nullptr && pNode->next->val != value)
            pNode = pNode->next;

        if(pNode->next != nullptr && pNode->next->val == value){
            pToDeleted = pNode->next;
            pNode->next = pNode->next->next;
        }
    }

    if(pToDeleted != nullptr){
        delete pToDeleted;
        pToDeleted = nullptr;
    }

    return;
}

反向打印链表

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
    ListNode* ReverseList(ListNode* p1) {
   
        ListNode* p2 = nullptr; 	// p2为反转链表,初始化为nullptr
        ListNode* pNext = nullptr;  // pNext为每次抛弃表头的p1指针
        
        while(p1){// 当p1指针不为空时
            pNext = p1->next;// pNext存放当前结点的下一结点的地址
            p1->next = p2; // 当前结点指向前一个结点——将p1结点给p2
            p2 = p1;   // p2往前走一个结点
            p1 = pNext; // p1 也走到下一个结点
        }
        return node;
    }
};


重建二叉树

#include <iostream>
#include <vector>
using namespace std;

// 结构体定义树的类型
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    //允许我们用 TreeNode node(int x)的方式把val赋值为x,left,right为NULL。
};

// 返回类型为树的指针,指针指向这棵树
// 输入参数为前序遍历和中序遍历的结果,类型为vector<int>的数组
//声明一个int向量以替代一维的数组:vector <int> a;(等于声明了一个int数组a[],大小没有指定,可以动态的向里面添加删除)。
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin){
	// 若遍历结果均为空 结束递归
    if(pre.size() == 0 || vin.size() == 0){
        return nullptr;
    }
    // 用根节点去初始化这棵树
    TreeNode *tree = new TreeNode(pre[0]);
    
    // 仅仅声明并初始化根节点
    int root = 0;
    int size = pre.size();
    
    // root记录了根节点在中序遍历结果中的下标
    for(int i = 0; i < size; i++){
        if(vin[i] == pre[0]){
            root = i;
        }
    }
    
    // 定义左右子树的前序遍历结果和中序遍历结果
    vector<int> pre_left, pre_right, vin_left, vin_right;
    // 依据规则填空
    /*push_back()函数的用法
函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
*/
    for(int i = 0; i < root; i++){//对两个遍历序列,添加左子树
        pre_left.push_back(pre[i+1]);
        vin_left.push_back(vin[i]);
    }
    // 依据规则填空
    for(int i = root + 1; i < size; i++){
        pre_right.push_back(pre[i]);
        vin_right.push_back(vin[i]);
    }
	
	// 递归的对左子树和右子树进行重建操作(输入他们各自对应的前序遍历结果和中序遍历结果即可)
    tree->left = reConstructBinaryTree(pre_left, vin_left);
    tree->right = reConstructBinaryTree(pre_right, vin_right);

	// 最终 返回指向该树的指针
    return tree;
}

int main() {
	// n为树的节点总数
    int n;
    cin >> n;
    vector<int> pre(n);  // 声明前序遍历结果
    vector<int> vin(n);    // 声明中序遍历结果
    for(int i = 0; i < n; i++) // 输入前序遍历结果
        cin >> pre[i];
    for(int i = 0; i < n; i++)// 输入中序遍历结果
        cin >> vin[i];
	// 重建二叉树
    TreeNode* myTree = reConstructBinaryTree(pre, vin);
    return 0;
}


java代码实现(较易)

import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length==0)
            return null;
            
        TreeNode tree=new TreeNode(pre[0]);
        
        for(int i=0;i<in.length;i++)
        {
            if(in[i]==pre[0])
            {
                tree.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
                tree.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
                    
            }
        }
        return tree;

栈和队列
用两个栈实现一个队列

插入一个元素:直接将元素插入stack1即可;
删除一个元素:当stack2不为空时 ,直接弹出栈顶元素,当stack2为空时,将stack1元素逐个弹出并压入stack2,然后在弹出栈顶元素;

class Solution
{
private:
	stack<int> stack1;
	stack<int> stack2;
	
public:
	void push(int node) {
		stack1.push(node);
	}
	
	int pop() {
		int x;
		if (stack2.empty())//stack2为空时,将stack1元素逐个弹出并压入stack2,然后在弹出stack2的栈顶元素
		{
			while (!stack1.empty())
			{
				stack2.push(stack1.top());//数字进入stack
				stack1.pop();//将数字弹出
			}
			x = stack2.top();//用x保存数字
			stack2.pop();//数字弹出
		}
		
		else    //stack2不为空时,直接弹出stack2的栈顶元素
		{
			x = stack2.top();
			stack2.pop();
		}
		return x;
	}
};

用两个队列实现一个栈

**两个队列的实现 仅在pop时处理
1、pop时,最后一个元素之前的元素全部出队,放到临时队列
2、pop最后一个元素
3、再将临时队列中的元素全部移回来

class MyStack {
private:
    queue<int> queue1;
    queue<int> queue2;
public:
    /** Initialize your data structure here. */
    MyStack() {}
    
    /** Push element x onto stack. */
    void push(int x) {
        queue1.push(x);//插入队列
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        while(queue1.size()>1){//将前size-1个元素移至中转队列
            queue2.push(queue1.front());
            queue1.pop();
        }
        
        int a=queue1.front();//保存队列数字
        queue1.pop();//pop操作!!!
        
        while(!queue2.empty()){//再将中转队列中的所有元素移回队列
            queue1.push(queue2.front());
            queue2.pop();
        }
        return a;
    }
    
    /** Get the top element. */
    int top() {
        return queue1.back();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queue1.empty();
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值