数据结构题集

一、时间复杂度

在这里插入图片描述

二、线性表

1.输出倒数第k个结点

在这里插入图片描述

从头到尾遍历单链表,并用指针q指向当前结点的前k个结点,当遍历到链表尾部时,q所指的结点即为倒数第k个结点

int findElem(LNode *head, int k)
{
	LNode *p = head->next, *q = head;
	int i = 1;
	while(p != NULL)
	{
		p = p->next;
		i++;
		
		if(i > k)
		{
			q = q->next;
		}
	}
	if (q == head)  //没有k个结点
	    return 0;
	else
	{
		cout << q->data << endl;
		return 1;
	} 
}

三、栈和队列

1.出栈方式

n个元素入栈的出栈方式:在这里插入图片描述

2.用仅有尾指针的循环单链表实现队列

//入队
void enQueue(LNode *&rear, int x)
{
    LNode *s = (LNode*)malloc(sizeof(LNode);
    s->data = x;
    s->next = rear->next;
    rear->next = s;
    rear = s;
}

//出队
int deQueue(LNode *&rear, int &x)
{
    LNode *s;
    if(rear->next == rear)
        return 0;
    else
    {
        s = rear->next->next;  //s指向开始结点
        rear->next->next = s->next;  //队头元素出栈
        x = s->data;
        if(s == rear)  //若出队后为空
            rear = rear->next;
        free(s);
        return 1;
    }
}

四、串

1.KMP算法

void getnext(Str substr, int next[])
{
    int i = 1, j = 0;
    next[i] = j;
    while(i < substr.length)
    {
        if(j == 0 || substr.ch[i] == substr.ch[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
            j = next[j];
    }
}

int KMP(Str str, Str substr, int next[])
{
    int i = 1, j = 0;
    while(i <= str.length && j <= substr.length)
    {
        if(j == 0 || str.ch[i] == substr.ch[j])
        {
            i++;
            j++;
        }
        else
            j = next[j];
    }
    if(j > substr.length)
        return i - substr.length;
    else
        return 0;
}

//改进
void getnextval(Str substr, int nextval[])
{
    int i = 1, j = 0;
    nextval[i] = j;
    while(i < substr.length)
    {
        if(j == 0 || substr.ch[i] == substr.ch[j])
        {
            i++;
            j++;
            if(substr.ch[i] != substr.ch[j])
                nextval[i] = j;
            else
                nextval[i] = nextval[j];
        }
        else
            j = nextval[j];
    }
}

在这里插入图片描述

五:树与二叉树

  1. 树的先序遍历对应于二叉树的先序遍历,树的后序遍历对应于二叉树的中序遍历

  2. 具有n个结点的完全二叉树的深度为⌊log2n⌋+1

  3. 用n个值组成一个赫夫曼树,会有2n-1个结点(n + n - 1)

  4. n个结点的线索二叉树有n-1个线索

  5. 度为m的赫夫曼树中,叶子结点数为n,则非叶子结点数为在这里插入图片描述

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

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

  8. A在这里插入图片描述

1.打印根节点到叶子结点的路径

在这里插入图片描述

char pathStack[maxSize];  //路径栈
void printPath(BTNode *p)
{
    int i, top = -1;  //top为栈顶指针
    if(p == NULL)  //空树
        return;
    pathStack[++top] = p->fata;  //上图(1)处,p是从上往下走,结点入栈
    if(p->lchild == NULL && p->rchild == NULL)  //叶子结点
    {
        //打印路径
        for(i = 0; i < top; i++)
            cout << pathStack[i] << "->";
    }
    printPath(p->lchild);
    printPath(p->rchild);
    top--;  //上图(3)处,p是从下往上走,结点出栈

考研真题

1.2020求三元组的最小距离

定义三元组(a,b,c)(a,b,c均为整数)的距离D =∣a−b∣+∣b−c∣+∣c−a∣。
给定3个非空整数集合S1,S2和S3,按升序分别存储在3个数组中。
请设计一种尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1,b∈S2,c∈S3)中的最小距离
例如,S1={-1, 0, 9},S2={-25, -10, 10, 11},S3={2, 9, 17, 30, 41},
则最小距离为2,相应的三元组为(9, 10, 9)。

在这里插入图片描述

int isMin(int x, int y, int z){
	if(x <= y && x <= z)
		return 1;
	else 
		return 0;
}
int main(){
	int S1[] = {-1,0,9};
	int S2[] = {-25, -10, 10, 11};
	int S3[] = {2, 9, 17, 30, 41};
	int res[3] = {0, 0, 0};
	int Length1 = sizeof(S1)/sizeof(S1[0]);
	int Length2 = sizeof(S2)/sizeof(S2[0]);
	int Length3 = sizeof(S3)/sizeof(S3[0]);
 
	int D = INT_MAX; 
	int i = 0, j = 0, k = 0; 
	while(i < Length1 && j < Length2 && k < Length3 && D > 0){
		int dis = abs(S1[i] - S2[j]) + abs(S2[j]- S3[k]) + abs(S3[k] - S1[i]);
		if(dis < D){
			 D = dis;
			 res[0] = S1[i];
			 res[1] = S2[j];
			 res[2] = S3[k];
		}
		if(isMin(S1[i],S2[j],S3[k])) i++;
		else if(isMin(S2[j],S3[k],S1[i])) j++;
		else k++;
	}
	cout << res[0] << " " << res[1] << " " << res[2] << " " << endl;
	return 0;
}

2.2019线性表的重新排列

在这里插入图片描述

1.用两个指针交替进行,找到链表的中间结点
2.将单链表的后半段逆置
3.从单链表的前后各取一个结点,按要求依次重排

void change_list(NODE* head)
{
     NODE * mid,*front, *tail, *temp;
     mid = temp = head;
     //寻找中间结点
     while (temp->next != NULL)
     {
         mid = mid->next;   //mid走一步
         temp = temp->next;     //temp走两步
         if (temp->next != NULL)
             temp = temp->next;
     }
     tail = mid->next;  //tail为后半不分链表的首结点
     mid->next = NULL;
     //后半段逆置
     while (tail != NULL)
     {
         temp = tail->next;
         tail->next = mid->next;
         mid->next = tail;
         tail = temp;
     }
     front = head->next;  //前半段的第一个数据结点,即插入点
     tail = mid->next;  //后半段的第一个数据结点
     //开始重组链表
     mid->next = NULL;
     while (tail != NULL)
     {
         temp = tail->next;
         tail->next = front->next;
         front->next = tail;
         front = tail->next;
         tail = temp;
     }
}    

在这里插入图片描述

3.2018统计未出现的最小正整数

在这里插入图片描述

int findMissMin(int A[], int n)
{
    int i, * B; //标记数组
    B = (int*)malloc(sizeof(int) * n);//分配空间
    memset(B, 0, sizeof(int) * n); //赋初值为0
    for (i = 0; i < n; i++)
        if (A[i] > 0 && A[i] <= n) //若A[i]的值介于1~n,则标记数组B
            B[A[i] - 1] = 1;
    for (i = 0; i < n; i++) //扫描数组B,找到目标值
        if (B[i] == 0) break;
    return i + 1; //返回结果
}

4.2017表达式树转为中缀表达式

在这里插入图片描述

void BtreeToE(BTree *root){
    BtreeToExp(root, 1); //根的高度为 1 
}
void BtreeToExp( BTree *root, int deep)
{
    if(root == NULL) return; //空结点返回
    else if(root->left==NULL&&root->right==NULL) //若为叶结点
        printf(%s”, root->data); //输出操作数,不加括号
    else{
        if(deep>l) 
            printf((); //若有子表达式则加1层括号
        BtreeToExp(root->left, deep+1);
        printf(%s”, root->data); //输出操作符
        BtreeToExp(root->right, deep+1);
        if(deep>l) 
            printf()); //若有子表达式则加1层括号
    } 
}

5.2015删除链表的绝对值相等的元素

在这里插入图片描述

typedef struct node {
    int data;
    struct node* link;
}NODE;
typedef NODE* PNODE;

void func(PNODE h, int n)
{
    PNODE p = h, r;
    int* q, m;
    q = (int*)malloc(sizeof(int) * (n + 1)); //申请n+1个位置的辅助空间
    for (int i = 0; i < n + 1; i++) //数组元素初值置0
        *(q + i) = 0;
    while (p->link != NULL)
    {
        m = p->link->data > 0 ? p->link->data : -p->link->data;
        if (*(q + m) == 0) //判断该结点的data是否已出现过
        {
            *(q + m) = 1; //首次出现
            p = p->link; //保留
        }
        else //重复出现
        {
            r = p->link; //删除
            p->link = r->link;
                free(r);
        }
    }
    free(q);
}

6.2014求带权路径长度(WPL)

在这里插入图片描述

typedef struct BiTNode {
    int weight;
    struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
int WPL(BiTree root) {
    return wpl_PreOrder(root, 0);
}
int wpl_PreOrder(BiTree root, int deep) {
    static int wpl = 0; //定义一个 static 变量存储 wpl
    if (root->lchild == NULL && root->rchild == NULL)  //若为叶子结点,累积 wpl
        wpl += deep * root->weight;
    if (root->lchild != NULL) //若左子树不空,对左子树递归遍历
        wpl_PreOrder(root->lchild, deep + 1);
    if (root->rchild != NULL) //若右子树不空,对右子树递归遍历
        wpl_PreOrder(root->rchild, deep + 1);
    return wpl;
}

7.2013找主元素

在这里插入图片描述

排序后的中位数

int majority(int A[], int n)
{
	//排序 
	sort(A,A + n);
	
	int num = 0, temp;
	if(n % 2 == 0)
	    temp = A[n / 2];
	else
	    temp = A[n / 2 + 1];
	
	for(int i = 0; i < n; i++)
	{
		if(A[i] == temp)
		    num++;
	}
	if(num > n / 2)
	    return temp;
	else
	    return -1;
	
	return 0;
}

8.2011两个升序序列的中位数

在这里插入图片描述

算法的基本设计思想如下:
分别求出序列 A 和 B 的中位数,设为 a 和 b,求序列 A 和 B 的中位数过程如下:

  • ① 若 a=b,则 a 或 b 即为所求中位数,算法结束。
  • ② 若 a<b,则舍弃序列 A 中较小的一半,同时舍弃序列 B 中较大的一半,要求舍弃的长度
    相等。
  • ③ 若 a>b,则舍弃序列 A 中较大的一半,同时舍弃序列 B 中较小的一半,要求舍弃的长度
    相等。

在保留的两个升序序列中,重复过程①、②、③,直到两个序列中只含一个元素时为止,较小者即为所求的中位数。

int M_Search(int A[], int B[], int n) {
    int s1 = 0, d1 = n - 1, m1, s2 = 1, d2 = n - 1, m2;
    //分别表示序列 A 和 B 的首位数、末位数和中位数
    while (s1 != d1 || s2 != d2) {
        m1 = (s1 + d1) / 2;
        m2 = (s2 + d2) / 2;
        if (A[m1] == B[m2])
            return A[m1]; //满足条件 1)
        if (A[m1] < B[m2]) { //满足条件 2)
            if ((s1 + d1) % 2 == 0) { //若元素个数为奇数
                s1 = m1; //舍弃 A 中间点以前的部分,且保留中间点
                d2 = m2; //舍弃 B 中间点以后的部分,且保留中间点
            }
            else { //元素个数为偶数
                s1 = m1 + 1; //舍弃 A 中间点及中间点以前部分
                d2 = m2; //舍弃 B 中间点以后部分且保留中间点
            }
        }
        else { //满足条件 3)
            if ((s1 + d1) % 2 == 0) { //若元素个数为奇数
                d1 = m1; //舍弃 A 中间点以后的部分,且保留中间点
                s2 = m2; //舍弃 B 中间点以前的部分,且保留中间点
            }
            else { //元素个数为偶数
                d1 = m1 + 1; //舍弃 A 中间点以后部分,且保留中间点
                s2 = m2; //舍弃 B 中间点及中间点以前部分
            }
        }
    }
    return A[s1] < B[s2] ? A[s1] : B[s2];
}

算法的时间复杂度为 O(log2n),空间复杂度为 O(1)

9.2010数组左移

在这里插入图片描述

将R中前P个元素逆置,再将其余的元素逆置,最后整体逆置

void reverse(SqList &L)
{
	int i,j;
	int temp;
	for(i = 0, j = L.length; i < j ; ++i, --j)
	{
		temp = L.data[i];
		L.data[i] = L.data[j];
		L.data[j] = temp;
	}
}

void RCR(int R[], int n, int P)
{
	if(p <= 0 || p >= n)
	    return;
	else
	{
		Reverse(R, 0, P - 1);
		Reverse(R, P, n - 1);
		Reverse(R, 0, n - 1);
	}
} 

习题

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

1.顺序表删除元素

已知长度为n的线性表L采用顺序存储结构,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素。

void  delnode(sqList &L, ElemType x)
{    int  k=0, i;
     for(int i=0; i<L.Length(); i++)
       if(L.data[i]!=x)
	      {
	          L.data[k]=L.data[i]; 
	          k++;
	      }
}

2.稀疏矩阵的转置

设稀疏矩阵如下图所示:
(1)给出该图的三元组表A.
(2)给出该图的转制矩阵的三元组表B.
(3)当采用快速转置算法,请写出辅助数组rowSize和rowStart
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SpareMatrix FastTranspos()
{
    int* rowSize = new int[Cols];  //统计各列非零元素
    int* rowStart = new int[Cols];  //预计转置后各行存放位置
    SpareMatrix b;  //存放转置结果
    b.Rows = Cols;
    b.Cols = Rows;
    b.Terms = Terms;
    if(Terms > 0)
    {
        int i, j;
        for(i = 0; i < Cols;  i++)
            rowSize[i] = 0;  //清零
        for(i = 0; i < Terms;  i++)
            rowSize[smArray[i].col]++;  //统计
        rowStart[0] = 0;
        for(i = 1; i < Cols;  i++)
            rowStart[i] = rowSize[i - 1] + rowStart[i - 1];
        for(i = 0; i < Terms; i++)
        {
            j = rowStart[smArray[i].col];
            b.smArray[j].row = smArray[i].col;
            b.smArray[j].col = smArray[i].row;
            b.smArray[j].value = smArray[i].value;
            rowStart[smArray[i].col]++;
        }
    }
    delete[]rowStart;
    delete[]rowSize;
    return b;
}

3.链表的就地逆置

就地:辅助空间为O(1)

void Reversel(LinkList*& L)
{
    LinkLst* p = L->link, *q;
    L->link = NULL;
    while(p != NULL)
    {
        q = p->link;  //保存当前节点的下一个节点
        p->link = L->link;  //更新当前节点的指针域
        L->link = p;  //更新当前节点上一个节点的位置
        p = q;  //更新当前节点的位置
    }
}

4.表达式匹配

设置一个栈,扫描表达式exp,遇到’(’,’[’,’{’时,将其入栈;遇到’)’,’]’,’}’时,将栈顶符号弹出,与之进行匹配。如果匹配,算法继续进行;否则 表达式的括号不匹配。当算法终止时,栈不为空,exp的括号不匹配;否则,exp成功匹配。

bool Match(char exp[], int n)
{
    char st[maxSize];
    int i, top = -1;  //栈顶指针
    bool tag = true;
    while(i < n && tag)
    {
        if(exp[i] == '(' || exp[i] == '[' || exp[i] == '{')
        st[++top] = exp[i];
        else if(exp == ')')
            if(st[top] == '(')
                top--;
            else
                return false;
        else if(exp == ']')
            if(st[top] == '[')
                top--;
            else
                return false;
        else if(exp == '}')
            if(st[top] == '{')
                top--;
            else
                return false;
    }
    if(top >= 0)
        return false;
    else
        return true;
}

5.只有尾指针的循环队列

//入队
void EnQu(Qnode*& rear, ElemType x)
{
    Qnode* s = new Qnode;
    s->data = x;
    if(rear == NULL)
    {
        s->next = s;
        rear = s;
    }
    else
    {
        rear->next = s;
        rear = s;
    }
}
//出队
int DeQu(Qnode*& rear, ElemType& x)
{
    Qnode* q;
    if(rear == NULL)
        return 0;
    else if (rear->next == rear)
    {
        x = rear->data;
        delete rear;
        rear = NULL;
    }
    else
    {
        q = rear->next;
        x = q->data;
        rear->next = q->next;
        delete q;
    }
    return 1;
}

6.实现递归函数

在这里插入图片描述

stack[top][0] 存储F(n)d 值,
stack[top][1] 存储n的值,
stack[top][2] 存储F(n/2)的值
因此有如下关系:
stack[top][0] =stack[top][1]*stack[top][2];
stack[top][2] =stack[top-1][0];

int F1(int n)
{
	if (n == 0)
		return 1;
	else
		return n * F1(n / 2);
}
int F2(int n)
{
	if (n == 0)
		return 1;
	int stack[100][3];
	int top = 0;
	stack[top][1] = n;
	while (n != 0)
	{
		top++;
		n /= 2;
		stack[top][1] = n;
	}
	stack[top][0] = 1;
	while (top > 0)
	{
		int f = stack[top][0];
		top--;
		stack[top][2] = f;
		stack[top][0] = stack[top][1] * stack[top][2];
	}
	return stack[top][0];
}

7.广义表

假设广义表是由带头节点的链表存储,请画出广义表A(a, B((), C(1)))的存储结构;假设head,tail分别是求首元素操作和求尾表操作,求如何获得元素1.

在这里插入图片描述

8.平均查找长度

在这里插入图片描述

9.求所有简单路径

从顶点m出发遍历,若找到了顶点v,则输出path;否则找到m的相邻点w,若顶点没有访问,则从w开始递归遍历。

void PathAll1(Graph *G,int u,int m,int v,int path[]int d)
{
    ArcNode *p;
    int j, w;
    if(m == v) 
    { 
         for(j = 0; j <= d; j++) 
             cout << path[j] << ' '; 
         cout << endl;;
     }
     else
     {
         p=G->NodeTable[m].adj;
         while(p!=NULL)
         {
             w=p->dest;
             if(visited[w]==0)
             {
                 visited[w]=1;
                 path[d+1]=w;
                 Path(G, u, w, v, path, d+1);
                 visited[w]=0;
             }
             p=p->link;
         }  
     }
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值