commit b34bc341435fc4eab2f15daf3ac956727b39c281
Author: 3wish <hety13@chinaunicom.cn>
Date:   Wed Nov 29 19:31:30 2023 +0800

    stack

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cdd4b45
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.venv
+test/.pytest_cache/
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..c52158a
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,31 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredPackages">
+        <value>
+          <list size="18">
+            <item index="0" class="java.lang.String" itemvalue="autoflake" />
+            <item index="1" class="java.lang.String" itemvalue="pytest" />
+            <item index="2" class="java.lang.String" itemvalue="pytest-cov" />
+            <item index="3" class="java.lang.String" itemvalue="mypy" />
+            <item index="4" class="java.lang.String" itemvalue="black" />
+            <item index="5" class="java.lang.String" itemvalue="h11" />
+            <item index="6" class="java.lang.String" itemvalue="isort" />
+            <item index="7" class="java.lang.String" itemvalue="pytest-asyncio" />
+            <item index="8" class="java.lang.String" itemvalue="codecov" />
+            <item index="9" class="java.lang.String" itemvalue="pandas" />
+            <item index="10" class="java.lang.String" itemvalue="Django" />
+            <item index="11" class="java.lang.String" itemvalue="SQLAlchemy" />
+            <item index="12" class="java.lang.String" itemvalue="DBUtils" />
+            <item index="13" class="java.lang.String" itemvalue="openpyxl-3.1.2" />
+            <item index="14" class="java.lang.String" itemvalue="PyMySQL" />
+            <item index="15" class="java.lang.String" itemvalue="uwsgi" />
+            <item index="16" class="java.lang.String" itemvalue="djangorestframework" />
+            <item index="17" class="java.lang.String" itemvalue="django-cors-headers" />
+          </list>
+        </value>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..b4319e3
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (pygorithm)" project-jdk-type="Python SDK" />
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..56e11be
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/pygorithm.iml" filepath="$PROJECT_DIR$/.idea/pygorithm.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/pygorithm.iml b/.idea/pygorithm.iml
new file mode 100644
index 0000000..eda47f2
--- /dev/null
+++ b/.idea/pygorithm.iml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="jdk" jdkName="Python 3.11 (pygorithm)" jdkType="Python SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..d26c9f0
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,12 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+pytest = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.11"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..83c1cb2
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,61 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "922e82e69ac92d524e9aec65cbead9fdef4cdb3fcff8f459d8998bfd7bd6a67f"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.11"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "colorama": {
+            "hashes": [
+                "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
+                "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
+            ],
+            "markers": "sys_platform == 'win32'",
+            "version": "==0.4.6"
+        },
+        "iniconfig": {
+            "hashes": [
+                "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
+                "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==2.0.0"
+        },
+        "packaging": {
+            "hashes": [
+                "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
+                "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==23.2"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12",
+                "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.3.0"
+        },
+        "pytest": {
+            "hashes": [
+                "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac",
+                "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"
+            ],
+            "index": "pypi",
+            "version": "==7.4.3"
+        }
+    },
+    "develop": {}
+}
diff --git a/stack/__pycache__/infix_conversion.cpython-311.pyc b/stack/__pycache__/infix_conversion.cpython-311.pyc
new file mode 100644
index 0000000..be2b077
Binary files /dev/null and b/stack/__pycache__/infix_conversion.cpython-311.pyc differ
diff --git a/stack/__pycache__/number_conversion.cpython-311.pyc b/stack/__pycache__/number_conversion.cpython-311.pyc
new file mode 100644
index 0000000..0bb7cd0
Binary files /dev/null and b/stack/__pycache__/number_conversion.cpython-311.pyc differ
diff --git a/stack/__pycache__/parentheses.cpython-311.pyc b/stack/__pycache__/parentheses.cpython-311.pyc
new file mode 100644
index 0000000..025f39a
Binary files /dev/null and b/stack/__pycache__/parentheses.cpython-311.pyc differ
diff --git a/stack/__pycache__/stack.cpython-311.pyc b/stack/__pycache__/stack.cpython-311.pyc
new file mode 100644
index 0000000..c62d994
Binary files /dev/null and b/stack/__pycache__/stack.cpython-311.pyc differ
diff --git a/stack/infix_conversion.py b/stack/infix_conversion.py
new file mode 100644
index 0000000..5baf349
--- /dev/null
+++ b/stack/infix_conversion.py
@@ -0,0 +1,78 @@
+"""
+1. 经观察可发现操作数的相对位置是不变的,变化的只是操作符
+2. 中转前:
+    (1)。 如果是一个操作数,则看一眼操作符栈中的栈顶是否是 */
+        (1.1). 如果是 */,则先拿到这个操作数,再从操作数栈弹出一个数
+                再弹出操作符,拼接后放入操作数栈顶
+        (1.2)。如果是 +-,则将此操作数入操作数栈
+    (2). 如果是一个 ( + - * /,直接入操作符栈顶
+    (3). 如果是 ),则执行出栈,直到遇到 (,出栈的步骤跟 (1.1)
+3. 中转后:
+    (1).
+"""
+
+from .stack import Stack
+
+
+def infix_to_prefix(infix: str):
+    op_stack = Stack[str]()
+    num_stack = Stack[str]()
+
+    for item in infix:
+        if item in ['(', '*', '+', '-', '/']:
+            op_stack.push(item)
+        elif '0' <= item <= 'z':
+            if op_stack.peek() in ['*', '/']:
+                temp = num_stack.pop()
+                op = op_stack.pop()
+                num_stack.push(op + temp + item)
+            else:
+                num_stack.push(item)
+        elif item == ')':
+            op = op_stack.pop()
+            while op != '(':
+                temp1 = num_stack.pop()
+                temp2 = num_stack.pop()
+                num_stack.push(op + temp2 + temp1)
+                op = op_stack.pop()
+    if not op_stack.is_empty():
+        i1 = num_stack.pop()
+        i2 = num_stack.pop()
+        op = op_stack.pop()
+        return op + i2 + i1
+    else:
+        return num_stack.pop()
+
+
+def infix_to_postfix(infix: str):
+    op_stack = Stack[str]()
+    num_stack = Stack[str]()
+
+    for item in infix:
+        if item in ['(', '*', '+', '-', '/']:
+            op_stack.push(item)
+        elif '0' <= item <= 'z':
+            if op_stack.peek() in ['*', '/']:
+                temp = num_stack.pop()
+                op = op_stack.pop()
+                num_stack.push(temp + item + op)
+            else:
+                num_stack.push(item)
+        elif item == ')':
+            op = op_stack.pop()
+            while op != '(':
+                temp1 = num_stack.pop()
+                temp2 = num_stack.pop()
+                num_stack.push(temp2 + temp1 + op)
+                op = op_stack.pop()
+    if not op_stack.is_empty():
+        i1 = num_stack.pop()
+        i2 = num_stack.pop()
+        op = op_stack.pop()
+        return i2 + i1 + op
+    else:
+        return num_stack.pop()
+
+
+res = infix_to_prefix('a+b*c')
+print(res)
diff --git a/stack/number_conversion.py b/stack/number_conversion.py
new file mode 100644
index 0000000..6a76df7
--- /dev/null
+++ b/stack/number_conversion.py
@@ -0,0 +1,24 @@
+"""
+进制转换:
+1. 将十进制转换为二(八、十六)进制
+2. 对2取余,余数存入栈中
+3. 将取余得的商,重复第2步,直到商为零
+4. 将栈中的数取出即是转换为二进制的结果
+"""
+
+from .stack import Stack
+
+
+def decimal_to_any(num: int, base: 2 | 8 | 16) -> str:
+    stack = Stack[str]()
+
+    while num > 0:
+        mod: int = num % base
+        num //= base
+        stack.push(str(mod))
+
+    res = ''
+    while not stack.is_empty():
+        res += stack.pop()
+
+    return res
diff --git a/stack/parentheses.py b/stack/parentheses.py
new file mode 100644
index 0000000..7fa4dcb
--- /dev/null
+++ b/stack/parentheses.py
@@ -0,0 +1,27 @@
+"""
+测试括号是否匹配:
+1. 左括号数必须与右括号数匹配
+2. 从左到右匹配,用栈保存每个左括号
+3. 每遇到一个右括号,则左括号执行出栈操作
+4. 匹配成功:结束时若栈空则
+5. 匹配失败:栈未空(左 > 右)或者在结束之前栈已空(右 > 左)
+"""
+
+from .stack import Stack
+
+
+def par_match(parentheses: str) -> bool:
+    stack = Stack[str]()
+
+    for i in parentheses:
+        if i == '(':
+            stack.push(i)
+        elif i == ')':
+            if stack.is_empty():
+                return False
+            stack.pop()
+
+    if stack.is_empty():
+        return True
+    else:
+        return False
diff --git a/stack/stack.py b/stack/stack.py
new file mode 100644
index 0000000..4fdf43b
--- /dev/null
+++ b/stack/stack.py
@@ -0,0 +1,32 @@
+from typing import Generic, TypeVar
+
+T = TypeVar('T')
+
+
+class Stack(Generic[T]):
+    def __init__(self):
+        self.top: int = 0
+        self.data: list[T] = []
+
+    def push(self, value: T):
+        self.data.append(value)
+        self.top += 1
+
+    def pop(self) -> T | None:
+        if self.top > 0:
+            self.top -= 1
+            return self.data.pop()
+        else:
+            return None
+
+    def peek(self) -> T | None:
+        if self.top > 0:
+            return self.data[self.top - 1]
+        else:
+            return None
+
+    def is_empty(self) -> bool:
+        return self.top == 0
+
+    def size(self) -> int:
+        return self.top
diff --git a/test/__pycache__/test_stack.cpython-311-pytest-7.4.3.pyc b/test/__pycache__/test_stack.cpython-311-pytest-7.4.3.pyc
new file mode 100644
index 0000000..9d2f95b
Binary files /dev/null and b/test/__pycache__/test_stack.cpython-311-pytest-7.4.3.pyc differ
diff --git a/test/test_stack.py b/test/test_stack.py
new file mode 100644
index 0000000..0a29bc8
--- /dev/null
+++ b/test/test_stack.py
@@ -0,0 +1,58 @@
+from stack.parentheses import par_match
+from stack.number_conversion import decimal_to_any
+from stack.infix_conversion import infix_to_prefix, infix_to_postfix
+
+
+def test_par_match1():
+    parentheses = '((()))(()()'
+    assert not par_match(parentheses)
+
+
+def test_par_match2():
+    parentheses = '((()))(())'
+    assert par_match(parentheses)
+
+
+def test_par_match3():
+    parentheses = '(((5+3)))*((4-2))'
+    assert par_match(parentheses)
+
+
+def test_to_binary1():
+    num = 5
+    assert decimal_to_any(num, 2) == '101'
+
+
+def test_to_binary2():
+    num = 10
+    assert decimal_to_any(num, 2) == '1010'
+
+
+def test_infix_to_prefix():
+    infix = 'a+b*c'
+    assert infix_to_prefix(infix) == '+a*bc'
+
+
+def test_infix_to_prefix_with_parentheses1():
+    infix = '(a+b)*c'
+    assert infix_to_prefix(infix) == '*+abc'
+
+
+def test_infix_to_prefix_with_parentheses2():
+    infix = '(a+b)*(c-d)'
+    assert infix_to_prefix(infix) == '*+ab-cd'
+
+
+def test_infix_to_postfix():
+    infix = 'a+b*c'
+    assert infix_to_postfix(infix) == 'abc*+'
+
+
+def test_infix_to_postfix_with_parentheses1():
+    infix = '(a+b)*c'
+    assert infix_to_postfix(infix) == 'ab+c*'
+
+
+def test_infix_to_postfix_with_parentheses2():
+    infix = '(a+b)/(c-d)'
+    assert infix_to_postfix(infix) == 'ab+cd-/'
\ No newline at end of file